// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#include "vm/debugger.h"

#include "include/dart_api.h"

#include "vm/bytecode_reader.h"
#include "vm/closure_functions_cache.h"
#include "vm/code_descriptors.h"
#include "vm/code_patcher.h"
#include "vm/compiler/api/deopt_id.h"
#include "vm/compiler/assembler/disassembler.h"
#include "vm/compiler/assembler/disassembler_kbc.h"
#include "vm/compiler/jit/compiler.h"
#include "vm/dart_entry.h"
#include "vm/flags.h"
#include "vm/globals.h"
#include "vm/isolate_reload.h"
#include "vm/json_stream.h"
#include "vm/kernel.h"
#include "vm/longjump.h"
#include "vm/message_handler.h"
#include "vm/object.h"
#include "vm/object_store.h"
#include "vm/os.h"
#include "vm/parser.h"
#include "vm/port.h"
#include "vm/runtime_entry.h"
#include "vm/service.h"
#include "vm/service_event.h"
#include "vm/service_isolate.h"
#include "vm/stack_frame.h"
#include "vm/stack_trace.h"
#include "vm/stub_code.h"
#include "vm/symbols.h"
#include "vm/thread_interrupter.h"
#include "vm/timeline.h"
#include "vm/token_position.h"
#include "vm/visitor.h"
#include "vm/zone_text_buffer.h"

#if !defined(DART_PRECOMPILED_RUNTIME)
#include "vm/deopt_instructions.h"
#endif  // !defined(DART_PRECOMPILED_RUNTIME)

namespace dart {

DEFINE_FLAG(bool,
            trace_debugger_stacktrace,
            false,
            "Trace debugger stacktrace collection");
DEFINE_FLAG(bool, trace_rewind, false, "Trace frame rewind");
DEFINE_FLAG(bool, verbose_debug, false, "Verbose debugger messages");

DECLARE_FLAG(bool, trace_deoptimization);
DECLARE_FLAG(bool, warn_on_pause_with_no_debugger);

#ifndef PRODUCT

// Create an unresolved breakpoint in given token range and script.
BreakpointLocation::BreakpointLocation(
    Debugger* debugger,
    const GrowableHandlePtrArray<const Script>& scripts,
    TokenPosition token_pos,
    TokenPosition end_token_pos,
    intptr_t requested_line_number,
    intptr_t requested_column_number)
    : debugger_(debugger),
      scripts_(MallocGrowableArray<ScriptPtr>(scripts.length())),
      url_(scripts.At(0).url()),
      line_number_lock_(new SafepointRwLock()),
      line_number_(-1),  // lazily computed
      token_pos_(token_pos),
      end_token_pos_(end_token_pos),
      next_(nullptr),
      conditions_(nullptr),
      requested_line_number_(requested_line_number),
      requested_column_number_(requested_column_number),
      code_token_pos_(TokenPosition::kNoSource) {
  ASSERT(scripts.length() > 0);
  ASSERT(token_pos.IsReal());
  for (intptr_t i = 0; i < scripts.length(); ++i) {
    scripts_.Add(scripts.At(i).ptr());
  }
}

// Create a latent breakpoint at given url and line number.
BreakpointLocation::BreakpointLocation(Debugger* debugger,
                                       const String& url,
                                       intptr_t requested_line_number,
                                       intptr_t requested_column_number)
    : debugger_(debugger),
      scripts_(MallocGrowableArray<ScriptPtr>(0)),
      url_(url.ptr()),
      line_number_lock_(new SafepointRwLock()),
      line_number_(-1),  // lazily computed
      token_pos_(TokenPosition::kNoSource),
      end_token_pos_(TokenPosition::kNoSource),
      next_(nullptr),
      conditions_(nullptr),
      requested_line_number_(requested_line_number),
      requested_column_number_(requested_column_number),
      code_token_pos_(TokenPosition::kNoSource) {
  ASSERT(requested_line_number_ >= 0);
}

BreakpointLocation::~BreakpointLocation() {
  Breakpoint* bpt = breakpoints();
  while (bpt != nullptr) {
    Breakpoint* temp = bpt;
    bpt = bpt->next();
    delete temp;
  }
}

bool BreakpointLocation::AnyEnabled() const {
  Breakpoint* bpt = breakpoints();
  while (bpt != nullptr) {
    if (bpt->is_enabled()) {
      return true;
    }
    bpt = bpt->next();
  }
  return false;
}

void BreakpointLocation::SetResolved(const Function& func,
                                     TokenPosition token_pos) {
#if defined(DEBUG)
  const Script& func_script = Script::Handle(func.script());
  const String& func_url = String::Handle(func_script.url());
  const String& script_url = String::Handle(url_);
  ASSERT(script_url.Equals(func_url));
#endif  // defined(DEBUG)
  ASSERT(!IsLatent());
  ASSERT(token_pos.IsWithin(func.token_pos(), func.end_token_pos()));
  ASSERT(func.is_debuggable());
  token_pos_.store(token_pos);
  end_token_pos_.store(token_pos);
  code_token_pos_ = token_pos;
}

void BreakpointLocation::GetCodeLocation(Script* script,
                                         TokenPosition* pos) const {
  if (IsLatent()) {
    *script = Script::null();
    *pos = TokenPosition::kNoSource;
  } else {
    *script = this->script();
    *pos = token_pos_;
  }
}

intptr_t BreakpointLocation::line_number() {
  // Compute line number lazily since it causes scanning of the script.
  {
    SafepointReadRwLocker sl(Thread::Current(), line_number_lock());
    if (line_number_ >= 0) {
      return line_number_;
    }
  }
  SafepointWriteRwLocker sl(Thread::Current(), line_number_lock());
  if (line_number_ < 0) {
    Script::Handle(script()).GetTokenLocation(token_pos(), &line_number_);
  }
  return line_number_;
}

void Breakpoint::set_bpt_location(BreakpointLocation* new_bpt_location) {
  // Only latent breakpoints can be moved.
  ASSERT((new_bpt_location == nullptr) || bpt_location_->IsLatent());
  bpt_location_ = new_bpt_location;
}

void Breakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) {
  visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&closure_));
}

void BreakpointLocation::VisitObjectPointers(ObjectPointerVisitor* visitor) {
  for (intptr_t i = 0; i < scripts_.length(); ++i) {
    visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&scripts_.data()[i]));
  }
  visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&url_));

  Breakpoint* bpt = conditions_;
  while (bpt != nullptr) {
    bpt->VisitObjectPointers(visitor);
    bpt = bpt->next();
  }
}

void Breakpoint::PrintJSON(JSONStream* stream) {
  JSONObject jsobj(stream);
  jsobj.AddProperty("type", "Breakpoint");

  jsobj.AddFixedServiceId("breakpoints/%" Pd "", id());
  jsobj.AddProperty("enabled", enabled_);
  jsobj.AddProperty("breakpointNumber", id());
  jsobj.AddProperty("resolved", bpt_location_->IsResolved());
  if (bpt_location_->IsResolved()) {
    jsobj.AddLocation(bpt_location_);
  } else {
    jsobj.AddUnresolvedLocation(bpt_location_);
  }
}

void CodeBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) {
  visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&code_));
  visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&saved_value_));
}

const char* CodeBreakpoint::ToCString() const {
  if (breakpoint_locations_.length() == 0) {
    return "unlinked breakpoint";
  }

  char buffer[1024];
  BufferFormatter f(buffer, sizeof(buffer));
  // Pick the first, all other should have same script/line number.
  BreakpointLocation* breakpoint_location = breakpoint_locations_.At(0);
  String& source_url = String::Handle(breakpoint_location->url());
  intptr_t line_number = breakpoint_location->line_number();

  f.Printf("breakpoint at %s:%" Pd, source_url.ToCString(), line_number);
  return Thread::Current()->zone()->MakeCopyOfString(buffer);
}

ActivationFrame::ActivationFrame(uword pc,
                                 uword fp,
                                 uword sp,
                                 const Function& function,
                                 const Object& code_or_bytecode,
                                 const Array& deopt_frame,
                                 intptr_t deopt_frame_offset)
    : pc_(pc),
      fp_(fp),
      sp_(sp),
      code_or_bytecode_(Object::ZoneHandle(code_or_bytecode.ptr())),
      function_(Function::ZoneHandle(function.ptr())),
      closure_(Closure::null_closure()),
      deopt_frame_(Array::ZoneHandle(deopt_frame.ptr())),
      deopt_frame_offset_(deopt_frame_offset),
      kind_(kRegular),
      desc_indices_(8),
      pc_desc_(PcDescriptors::ZoneHandle()) {
  ASSERT(!function.IsNull());
  ASSERT(code_or_bytecode.IsCode() || code_or_bytecode.IsBytecode());
}

ActivationFrame::ActivationFrame(uword pc,
                                 const Function& function,
                                 const Object& code_or_bytecode,
                                 const Closure& closure)
    : pc_(pc),
      code_or_bytecode_(Object::ZoneHandle(code_or_bytecode.ptr())),
      function_(Function::ZoneHandle(function.ptr())),
      closure_(Closure::ZoneHandle(closure.ptr())),
      deopt_frame_(Array::empty_array()),
      deopt_frame_offset_(0),
      kind_(kAsyncAwaiter) {
  ASSERT(code_or_bytecode.IsCode() || code_or_bytecode.IsBytecode());
}

ActivationFrame::ActivationFrame(Kind kind)
    : code_or_bytecode_(Object::null_object()),
      function_(Function::null_function()),
      closure_(Closure::null_closure()),
      deopt_frame_(Array::empty_array()),
      deopt_frame_offset_(0),
      kind_(kind) {
  ASSERT(kind == kAsyncSuspensionMarker);
}

bool Debugger::NeedsIsolateEvents() {
  ASSERT(isolate_ == Isolate::Current());
  return !Isolate::IsSystemIsolate(isolate_) &&
         Service::isolate_stream.enabled();
}

bool Debugger::NeedsDebugEvents() {
  if (Isolate::Current() == nullptr) {
    // E.g., NoActiveIsolateScope.
    return false;
  }
  RELEASE_ASSERT(isolate_ == Isolate::Current());
  ASSERT(!Isolate::IsSystemIsolate(isolate_));
  return FLAG_warn_on_pause_with_no_debugger || Service::debug_stream.enabled();
}

static void InvokeEventHandler(ServiceEvent* event) {
  ASSERT(!event->IsPause());  // For pause events, call Pause instead.
  Service::HandleEvent(event, /*enter_safepoint*/ false);
}

ErrorPtr Debugger::PauseInterrupted() {
  return PauseRequest(ServiceEvent::kPauseInterrupted);
}

ErrorPtr Debugger::PausePostRequest() {
  return PauseRequest(ServiceEvent::kPausePostRequest);
}

ErrorPtr Debugger::PauseRequest(ServiceEvent::EventKind kind) {
  if (ignore_breakpoints_ || IsPaused()) {
    // We don't let the isolate get interrupted if we are already
    // paused or ignoring breakpoints.
    return Thread::Current()->StealStickyError();
  }
  ServiceEvent event(isolate_, kind);
  DebuggerStackTrace* trace = DebuggerStackTrace::Collect();
  if (trace->Length() > 0) {
    event.set_top_frame(trace->FrameAt(0));
  }
  CacheStackTraces(trace, DebuggerStackTrace::CollectAsyncAwaiters());
  set_resume_action(kContinue);
  Pause(&event);
  HandleSteppingRequest();
  ClearCachedStackTraces();

  // If any error occurred while in the debug message loop, return it here.
  NoSafepointScope no_safepoint;
  ErrorPtr error = Thread::Current()->StealStickyError();
  ASSERT((error == Error::null()) || error->IsUnwindError());
  return error;
}

void Debugger::SendBreakpointEvent(ServiceEvent::EventKind kind,
                                   Breakpoint* bpt) {
  if (NeedsDebugEvents()) {
    // TODO(turnidge): Currently we send single-shot breakpoint events
    // to the vm service.  Do we want to change this?
    ServiceEvent event(isolate_, kind);
    event.set_breakpoint(bpt);
    InvokeEventHandler(&event);
  }
}

void BreakpointLocation::AddBreakpoint(Breakpoint* bpt, Debugger* dbg) {
  bpt->set_next(breakpoints());
  set_breakpoints(bpt);
  bpt->Enable();
  dbg->group_debugger()->SyncBreakpointLocation(this);
  dbg->SendBreakpointEvent(ServiceEvent::kBreakpointAdded, bpt);
}

Breakpoint* BreakpointLocation::AddRepeated(Debugger* dbg) {
  return AddBreakpoint(dbg, Closure::Handle(), /*single_shot=*/false);
}

Breakpoint* BreakpointLocation::AddSingleShot(Debugger* dbg) {
  return AddBreakpoint(dbg, Closure::Handle(), /*single_shot=*/true);
}

Breakpoint* BreakpointLocation::AddBreakpoint(Debugger* dbg,
                                              const Closure& closure,
                                              bool single_shot) {
  Breakpoint* bpt = breakpoints();
  while (bpt != nullptr) {
    if ((bpt->closure() == closure.ptr()) &&
        (bpt->is_single_shot() == single_shot)) {
      break;
    }
    bpt = bpt->next();
  }
  if (bpt == nullptr) {
    bpt = new Breakpoint(dbg->nextId(), this, single_shot, closure);
    AddBreakpoint(bpt, dbg);
  }
  return bpt;
}

static const char* QualifiedFunctionName(const Function& func) {
  const String& func_name = String::Handle(func.name());
  Class& func_class = Class::Handle(func.Owner());
  String& class_name = String::Handle(func_class.Name());

  return OS::SCreate(Thread::Current()->zone(), "%s%s%s",
                     func_class.IsTopLevel() ? "" : class_name.ToCString(),
                     func_class.IsTopLevel() ? "" : ".", func_name.ToCString());
}

// Returns true if the function |func| overlaps the token range
// [|token_pos|, |end_token_pos|] in |script|.
static bool FunctionOverlaps(const Function& func,
                             const String& script_url,
                             TokenPosition token_pos,
                             TokenPosition end_token_pos) {
  const TokenPosition& func_start = func.token_pos();
  if (token_pos.IsWithin(func_start, func.end_token_pos()) ||
      func_start.IsWithin(token_pos, end_token_pos)) {
    // Check script equality last because it allocates handles as a side effect.
    Script& func_script = Script::Handle(func.script());
    String& url = String::Handle(func_script.url());
    return script_url.Equals(url);
  }
  return false;
}

static bool IsImplicitFunction(const Function& func) {
  switch (func.kind()) {
    case UntaggedFunction::kImplicitGetter:
    case UntaggedFunction::kImplicitSetter:
    case UntaggedFunction::kImplicitStaticGetter:
    case UntaggedFunction::kFieldInitializer:
    case UntaggedFunction::kMethodExtractor:
    case UntaggedFunction::kNoSuchMethodDispatcher:
    case UntaggedFunction::kInvokeFieldDispatcher:
    case UntaggedFunction::kIrregexpFunction:
    case UntaggedFunction::kRecordFieldGetter:
      return true;
    default:
      if (func.token_pos() == func.end_token_pos()) {
        // |func| could be an implicit constructor for example.
        return true;
      }
  }
  return false;
}

bool GroupDebugger::HasCodeBreakpointInFunctionUnsafe(const Function& func) {
  DEBUG_ASSERT(code_breakpoints_lock()->IsCurrentThreadReader() ||
               Thread::Current()->IsInStoppedMutatorsScope());
  CodeBreakpoint* cbpt = code_breakpoints_;
  while (cbpt != nullptr) {
    if (func.ptr() == cbpt->function()) {
      return true;
    }
    cbpt = cbpt->next_;
  }
  return false;
}

bool GroupDebugger::HasCodeBreakpointInFunction(const Function& func) {
  auto thread = Thread::Current();
  // Don't need to worry about the lock if mutators are stopped.
  if (thread->IsInStoppedMutatorsScope()) {
    return HasCodeBreakpointInFunctionUnsafe(func);
  } else {
    SafepointReadRwLocker sl(thread, code_breakpoints_lock());
    return HasCodeBreakpointInFunctionUnsafe(func);
  }
}

bool GroupDebugger::HasBreakpointInCode(const Code& code) {
  auto thread = Thread::Current();
  SafepointReadRwLocker sl(thread, code_breakpoints_lock());
  CodeBreakpoint* cbpt = code_breakpoints_;
  while (cbpt != nullptr) {
    if (code.ptr() == cbpt->code_) {
      return true;
    }
    cbpt = cbpt->next_;
  }
  return false;
}

void Debugger::PrintBreakpointsToJSONArray(JSONArray* jsarr) const {
  PrintBreakpointsListToJSONArray(breakpoint_locations_, jsarr);
  PrintBreakpointsListToJSONArray(latent_locations_, jsarr);
}

void Debugger::PrintBreakpointsListToJSONArray(BreakpointLocation* sbpt,
                                               JSONArray* jsarr) const {
  while (sbpt != nullptr) {
    Breakpoint* bpt = sbpt->breakpoints();
    while (bpt != nullptr) {
      jsarr->AddValue(bpt);
      bpt = bpt->next();
    }
    sbpt = sbpt->next_;
  }
}

void Debugger::PrintSettingsToJSONObject(JSONObject* jsobj) const {
  // This won't cut it when we support filtering by class, etc.
  switch (GetExceptionPauseInfo()) {
    case kNoPauseOnExceptions:
      jsobj->AddProperty("_exceptions", "none");
      break;
    case kPauseOnAllExceptions:
      jsobj->AddProperty("_exceptions", "all");
      break;
    case kPauseOnUnhandledExceptions:
      jsobj->AddProperty("_exceptions", "unhandled");
      break;
    default:
      UNREACHABLE();
  }
}

ActivationFrame::Relation ActivationFrame::CompareTo(bool is_interpreted,
                                                     uword fp,
                                                     uword other_fp) {
  if (fp == other_fp) {
    return kSelf;
  }
#if defined(DART_DYNAMIC_MODULES)
  if (is_interpreted) {
    // Unlike compiled code, interpreted stacks grow towards higher addresses.
    return fp > other_fp ? kCallee : kCaller;
  }
#endif
  return IsCalleeFrameOf(other_fp, fp) ? kCallee : kCaller;
}

StringPtr ActivationFrame::QualifiedFunctionName() {
  return String::New(::dart::QualifiedFunctionName(function()));
}

StringPtr ActivationFrame::SourceUrl() {
  const Script& script = Script::Handle(SourceScript());
  return script.url();
}

ScriptPtr ActivationFrame::SourceScript() {
  return function().script();
}

LibraryPtr ActivationFrame::Library() {
  const Class& cls = Class::Handle(function().Owner());
  return cls.library();
}

void ActivationFrame::GetPcDescriptors() {
  ASSERT(!IsInterpreted());
  if (pc_desc_.IsNull()) {
    pc_desc_ = code().pc_descriptors();
    ASSERT(!pc_desc_.IsNull());
  }
}

// If not token_pos_initialized_, compute token_pos_, try_index_ and,
// if not IsInterpreted(), also compute deopt_id_.
TokenPosition ActivationFrame::TokenPos() {
  if (!token_pos_initialized_) {
    token_pos_ = TokenPosition::kNoSource;
    if (IsInterpreted()) {
      token_pos_ = bytecode().GetTokenIndexOfPC(pc_);
      try_index_ = bytecode().GetTryIndexAtPc(pc_);
    } else {
      GetPcDescriptors();
      PcDescriptors::Iterator iter(pc_desc_, UntaggedPcDescriptors::kAnyKind);
      const uword pc_offset = pc_ - code().PayloadStart();
      while (iter.MoveNext()) {
        if (iter.PcOffset() == pc_offset) {
          try_index_ = iter.TryIndex();
          token_pos_ = iter.TokenPos();
          deopt_id_ = iter.DeoptId();
          break;
        }
      }
    }
    token_pos_initialized_ = true;
  }
  return token_pos_;
}

intptr_t ActivationFrame::TryIndex() {
  if (!token_pos_initialized_) {
    TokenPos();  // Side effect: computes token_pos_initialized_, try_index_.
  }
  return try_index_;
}

intptr_t ActivationFrame::DeoptId() {
  ASSERT(!IsInterpreted());
  if (!token_pos_initialized_) {
    TokenPos();  // Side effect: computes token_pos_initialized_, try_index_.
  }
  return deopt_id_;
}

intptr_t ActivationFrame::LineNumber() {
  // Compute line number lazily since it causes scanning of the script.
  const TokenPosition& token_pos = TokenPos();
  if ((line_number_ < 0) && token_pos.IsReal()) {
    const Script& script = Script::Handle(SourceScript());
    script.GetTokenLocation(token_pos, &line_number_, &column_number_);
  }
  return line_number_;
}

intptr_t ActivationFrame::ColumnNumber() {
  // Compute column number lazily since it causes scanning of the script.
  const TokenPosition& token_pos = TokenPos();
  if ((column_number_ < 0) && token_pos.IsReal()) {
    const Script& script = Script::Handle(SourceScript());
    script.GetTokenLocation(token_pos, &line_number_, &column_number_);
  }
  return column_number_;
}

void ActivationFrame::GetVarDescriptors() {
  if (var_descriptors_.IsNull()) {
    if (IsInterpreted()) {
#if defined(DART_PRECOMPILED_RUNTIME)
      UNREACHABLE();
#else
      var_descriptors_ = bytecode().GetLocalVarDescriptors();
      ASSERT(!var_descriptors_.IsNull());
#endif
      return;
    }
    Code& unoptimized_code = Code::Handle(function().unoptimized_code());
    if (unoptimized_code.IsNull()) {
      if (function().ForceOptimize()) {
        var_descriptors_ = Object::empty_var_descriptors().ptr();
        return;
      }
      Thread* thread = Thread::Current();
      Zone* zone = thread->zone();
      const Error& error = Error::Handle(
          zone, Compiler::EnsureUnoptimizedCode(thread, function()));
      if (!error.IsNull()) {
        Exceptions::PropagateError(error);
      }
      unoptimized_code = function().unoptimized_code();
    }
    ASSERT(!unoptimized_code.IsNull());
    var_descriptors_ = unoptimized_code.GetLocalVarDescriptors();
    ASSERT(!var_descriptors_.IsNull());
  }
}

bool ActivationFrame::IsDebuggable() const {
  ASSERT(!function().IsNull());
  return Debugger::IsDebuggable(function());
}

void ActivationFrame::PrintContextLevelError(const char* message) {
  OS::PrintErr("Cannot locate context level: %s\n", message);
  OS::PrintErr("function %s\n", function().ToQualifiedCString());
  OS::PrintErr("pc_ %" Px "\n", pc_);
  OS::PrintErr("deopt_id_ %" Px "\n", deopt_id_);
  OS::PrintErr("context_level_ %" Px "\n", context_level_);
  OS::PrintErr("token_pos_ %s\n", token_pos_.ToCString());
  if (IsInterpreted() && bytecode().HasLocalVariablesInfo()) {
#if defined(DART_DYNAMIC_MODULES)
    Zone* const zone = Thread::Current()->zone();
    ZoneTextBuffer buffer(zone);
    KernelBytecodeDisassembler::PrintLocalVariablesInfo(
        zone, &buffer, bytecode(), PayloadStart());
    OS::PrintErr("%s\n", buffer.buffer());
#endif
  } else {
    DisassembleToStdout formatter;
    code().Disassemble(&formatter);
    PcDescriptors::Handle(code().pc_descriptors()).Print();
  }
  StackFrameIterator frames(ValidationPolicy::kDontValidateFrames,
                            Thread::Current(),
                            StackFrameIterator::kNoCrossThreadIteration);
  StackFrame* frame = frames.NextFrame();
  while (frame != nullptr) {
    OS::PrintErr("%s\n", frame->ToCString());
    frame = frames.NextFrame();
  }
  OS::Abort();
}

// Calculate the context level at the current pc of the frame.
intptr_t ActivationFrame::ContextLevel() {
  ASSERT(kind_ == kRegular);
  const Context& ctx = GetSavedCurrentContext();
  if (context_level_ < 0 && !ctx.IsNull()) {
    ASSERT(IsInterpreted() || !code().is_optimized());
    bool found = false;
    if (IsInterpreted()) {
#if defined(DART_DYNAMIC_MODULES) && !defined(PRODUCT) &&                      \
    !defined(DART_PRECOMPILED_RUNTIME)
      const intptr_t pc_offset = pc() - PayloadStart();
      DEBUG_ONLY(intptr_t closest_start = 0);
      bytecode::BytecodeLocalVariablesIterator local_vars(
          Thread::Current()->zone(), bytecode());
      while (local_vars.MoveNext()) {
        if (local_vars.IsScope()) {
          if (local_vars.StartPC() <= pc_offset &&
              pc_offset <= local_vars.EndPC()) {
            DEBUG_ASSERT(!found || local_vars.StartPC() > closest_start);
            found = true;
            context_level_ = local_vars.ContextLevel();
            DEBUG_ONLY(closest_start = local_vars.StartPC());
          } else if (local_vars.StartPC() > pc_offset) {
            // The scopes in the local variables info are ordered by starting
            // PC offset, so no need to search further.
            break;
          }
        }
      }
      if (!found) {
        PrintContextLevelError(
            "No Scope local variable info for the current PC");
      }
#else
      UNREACHABLE();
#endif
    } else {
      GetVarDescriptors();
      intptr_t var_desc_len = var_descriptors_.Length();
      // We store the deopt ids as real token positions.
      intptr_t deopt_id = DeoptId();
      if (deopt_id == DeoptId::kNone) {
        PrintContextLevelError("Missing deopt id");
      }
      const TokenPosition to_compare = TokenPosition::Deserialize(deopt_id);
      for (intptr_t cur_idx = 0; cur_idx < var_desc_len; cur_idx++) {
        UntaggedLocalVarDescriptors::VarInfo var_info;
        var_descriptors_.GetInfo(cur_idx, &var_info);
        const int8_t kind = var_info.kind();
        if ((kind == UntaggedLocalVarDescriptors::kContextLevel) &&
            to_compare.IsWithin(var_info.begin_pos, var_info.end_pos)) {
          context_level_ = var_info.index();
          found = true;
          break;
        }
      }
      if (!found) {
        PrintContextLevelError(
            "No ContextLevel var descriptor that contains the deopt id");
      }
    }
    ASSERT(context_level_ >= 0);
  }
  return context_level_;
}

bool ActivationFrame::HandlesException(const Instance& exc_obj) {
  if (kind_ == kAsyncSuspensionMarker) {
    return false;
  }
  intptr_t try_index = TryIndex();
  auto& handlers = ExceptionHandlers::Handle();
  if (IsInterpreted()) {
    handlers = bytecode().exception_handlers();
  } else {
    handlers = code().exception_handlers();
  }
  ASSERT(!handlers.IsNull());
  if ((try_index < 0) && !handlers.has_async_handler()) {
    return false;
  }
  Array& handled_types = Array::Handle();
  AbstractType& type = Type::Handle();
  intptr_t num_handlers_checked = 0;
  while (try_index != kInvalidTryIndex) {
    // Detect circles in the exception handler data.
    num_handlers_checked++;
    ASSERT(num_handlers_checked <= handlers.num_entries());
    // Only consider user written handlers and ignore synthesized try/catch in
    // async methods as well as synthetic try/catch hiding inside try/finally.
    if (!handlers.IsGenerated(try_index)) {
      handled_types = handlers.GetHandledTypes(try_index);
      const intptr_t num_types = handled_types.Length();
      for (intptr_t k = 0; k < num_types; k++) {
        type ^= handled_types.At(k);
        ASSERT(!type.IsNull());
        // Uninstantiated types are not added to ExceptionHandlers data.
        ASSERT(type.IsInstantiated());
        if (type.IsDynamicType()) {
          return true;
        }
        if (exc_obj.IsInstanceOf(type, Object::null_type_arguments(),
                                 Object::null_type_arguments())) {
          return true;
        }
      }
    }
    try_index = handlers.OuterTryIndex(try_index);
  }

  return false;
}

// Get the saved current context of this activation.
const Context& ActivationFrame::GetSavedCurrentContext() {
  if (context_initialized_) return ctx_;
  context_initialized_ = true;
  GetVarDescriptors();
  intptr_t var_desc_len = var_descriptors_.Length();
  Object& obj = Object::Handle();
  for (intptr_t i = 0; i < var_desc_len; i++) {
    UntaggedLocalVarDescriptors::VarInfo var_info;
    var_descriptors_.GetInfo(i, &var_info);
    const int8_t kind = var_info.kind();
    if (kind == UntaggedLocalVarDescriptors::kSavedCurrentContext) {
      if (FLAG_trace_debugger_stacktrace) {
        OS::PrintErr("\tFound saved current ctx at index %d\n",
                     var_info.index());
      }
      const auto variable_index = VariableIndex(var_info.index());
      obj = GetStackVar(variable_index);
      if (function_.IsImplicitInstanceClosureFunction()) {
        // The context of an implicit instance closure contains only one slot
        // for "this", so the instance is stored as the context and accessed
        // directly by generated code to avoid allocating a separate context.
        // Here, we create the elided context object for debugging purposes.
        ctx_ = Context::New(1);
        ctx_.SetAt(0, obj);
      } else if (obj.IsClosure()) {
        ASSERT(function().IsClosureCallDispatcher());
        // Closure.call frames.
        ctx_ = Closure::Cast(obj).GetContext();
      } else if (obj.IsContext()) {
        ctx_ = Context::Cast(obj).ptr();
      } else {
        ASSERT(obj.IsNull() || obj.ptr() == Object::optimized_out().ptr());
        ctx_ = Context::null();
      }
      return ctx_;
    }
  }
  return ctx_;
}

ActivationFrame* DebuggerStackTrace::GetHandlerFrame(
    const Instance& exc_obj) const {
  if (FLAG_trace_debugger_stacktrace) {
    OS::PrintErr("GetHandlerFrame(%s)\n", exc_obj.ToCString());
  }

  for (intptr_t frame_index = 0; frame_index < Length(); frame_index++) {
    ActivationFrame* frame = FrameAt(frame_index);
    const bool can_handle = frame->HandlesException(exc_obj);
    if (FLAG_trace_debugger_stacktrace) {
      OS::PrintErr("    #%04" Pd " (%s) %s", frame_index,
                   can_handle ? "+" : "-", frame->ToCString());
    }
    if (can_handle) {
      return frame;
    }
  }
  return nullptr;
}

void ActivationFrame::GetDescIndices() {
  if (vars_initialized_) {
    return;
  }
  GetVarDescriptors();

  TokenPosition activation_token_pos = TokenPos();
  if (!activation_token_pos.IsDebugPause() || kind_ != kRegular) {
    // We don't have a token position for this frame, so can't determine
    // which variables are visible.
    vars_initialized_ = true;
    return;
  }

  GrowableArray<String*> var_names(8);
  intptr_t var_desc_len = var_descriptors_.Length();
  for (intptr_t cur_idx = 0; cur_idx < var_desc_len; cur_idx++) {
    ASSERT(var_names.length() == desc_indices_.length());
    UntaggedLocalVarDescriptors::VarInfo var_info;
    var_descriptors_.GetInfo(cur_idx, &var_info);
    const int8_t kind = var_info.kind();
    if ((kind != UntaggedLocalVarDescriptors::kStackVar) &&
        (kind != UntaggedLocalVarDescriptors::kContextVar)) {
      continue;
    }
    if (!activation_token_pos.IsWithin(var_info.begin_pos, var_info.end_pos)) {
      continue;
    }
    if ((kind == UntaggedLocalVarDescriptors::kContextVar) &&
        (ContextLevel() < var_info.scope_id)) {
      // The variable is textually in scope but the context level
      // at the activation frame's PC is lower than the context
      // level of the variable. The context containing the variable
      // has already been removed from the chain. This can happen when we
      // break at a return statement, since the contexts get discarded
      // before the debugger gets called.
      continue;
    }
    // The current variable is textually in scope. Now check whether
    // there is another local variable with the same name that shadows
    // or is shadowed by this variable.
    String& var_name = String::Handle(var_descriptors_.GetName(cur_idx));
    intptr_t indices_len = desc_indices_.length();
    bool name_match_found = false;
    for (intptr_t i = 0; i < indices_len; i++) {
      if (var_name.Equals(*var_names[i])) {
        // Found two local variables with the same name. Now determine
        // which one is shadowed.
        name_match_found = true;
        UntaggedLocalVarDescriptors::VarInfo i_var_info;
        var_descriptors_.GetInfo(desc_indices_[i], &i_var_info);
        if (i_var_info.begin_pos < var_info.begin_pos) {
          // The variable we found earlier is in an outer scope
          // and is shadowed by the current variable. Replace the
          // descriptor index of the previously found variable
          // with the descriptor index of the current variable.
          desc_indices_[i] = cur_idx;
        } else {
          // The variable we found earlier is in an inner scope
          // and shadows the current variable. Skip the current
          // variable. (Nothing to do.)
        }
        break;  // Stop looking for name matches.
      }
    }
    if (!name_match_found) {
      // No duplicate name found. Add the current descriptor index to the
      // list of visible variables.
      desc_indices_.Add(cur_idx);
      var_names.Add(&var_name);
    }
  }
  vars_initialized_ = true;
}

intptr_t ActivationFrame::NumLocalVariables() {
  GetDescIndices();
  return desc_indices_.length();
}

DART_FORCE_INLINE static ObjectPtr GetVariableValue(uword addr) {
  return *reinterpret_cast<ObjectPtr*>(addr);
}

// Caution: GetParameter only works for fixed parameters.
ObjectPtr ActivationFrame::GetParameter(intptr_t index) {
  intptr_t num_parameters = function().num_fixed_parameters();
  ASSERT(0 <= index && index < num_parameters);

  // fp will be a nullptr if the frame isn't active on the stack.
  if (fp() == 0) {
    return Object::null();
  }

  if (function().MakesCopyOfParameters()) {
    // Function parameters are copied to a fixed place in the callee's frame.
    if (function().IsSuspendableFunction()) {
      ++index;  // Skip slot reserved for :suspend_state variable.
    }
    if (IsInterpreted()) {
      return GetStackVar(VariableIndex(-index));
    }
    return GetVariableValue(LocalVarAddress(
        fp(), runtime_frame_layout.FrameSlotForVariableIndex(-index)));
  } else {
    intptr_t reverse_index = num_parameters - index;
    if (IsInterpreted()) {
      return GetStackVar(VariableIndex(reverse_index));
    }
    return GetVariableValue(ParamAddress(fp(), reverse_index));
  }
}

ClosurePtr ActivationFrame::GetClosure() {
  ASSERT(function().IsClosureFunction());
  Object& param = Object::Handle(GetParameter(0));
  ASSERT(param.IsInstance());
  ASSERT(Instance::Cast(param).IsClosure());
  return Closure::Cast(param).ptr();
}

ObjectPtr ActivationFrame::GetSuspendStateVar() {
  ASSERT(function().IsSuspendableFunction());
  return GetStackVar(VariableIndex(SuspendState::kSuspendStateVarIndex));
}

ObjectPtr ActivationFrame::GetSuspendableFunctionData() {
  Object& suspend_state = Object::Handle(GetSuspendStateVar());
  if (suspend_state.IsSuspendState()) {
    return SuspendState::Cast(suspend_state).function_data();
  }
  return suspend_state.ptr();
}

ObjectPtr ActivationFrame::GetStackVar(VariableIndex variable_index) {
  if (IsInterpreted()) {
    intptr_t slot_index = -variable_index.value();
    if (slot_index < 0) {
      slot_index -= kKBCParamEndSlotFromFp;  // Accessing a parameter.
    }
    return GetVariableValue(fp() + slot_index * kWordSize);
  }
  const intptr_t slot_index =
      runtime_frame_layout.FrameSlotForVariableIndex(variable_index.value());
  if (deopt_frame_.IsNull()) {
    return GetVariableValue(LocalVarAddress(fp(), slot_index));
  } else {
    return deopt_frame_.At(LocalVarIndex(deopt_frame_offset_, slot_index));
  }
}

bool ActivationFrame::IsRewindable() const {
  if (deopt_frame_.IsNull()) {
    return true;
  }
  // TODO(turnidge): This is conservative.  It looks at all values in
  // the deopt_frame_ even though some of them may correspond to other
  // inlined frames.
  Object& obj = Object::Handle();
  for (int i = 0; i < deopt_frame_.Length(); i++) {
    obj = deopt_frame_.At(i);
    if (obj.ptr() == Object::optimized_out().ptr()) {
      return false;
    }
  }
  return true;
}

void ActivationFrame::PrintContextMismatchError(intptr_t ctx_slot,
                                                intptr_t frame_ctx_level,
                                                intptr_t var_ctx_level) {
  OS::PrintErr(
      "-------------------------\n"
      "Encountered context mismatch\n"
      "\tctx_slot: %" Pd
      "\n"
      "\tframe_ctx_level: %" Pd
      "\n"
      "\tvar_ctx_level: %" Pd "\n\n",
      ctx_slot, frame_ctx_level, var_ctx_level);

  OS::PrintErr(
      "-------------------------\n"
      "Current frame:\n%s\n",
      this->ToCString());

  OS::PrintErr(
      "-------------------------\n"
      "Context contents:\n");
  const Context& ctx = GetSavedCurrentContext();
  ctx.Dump(8);

  OS::PrintErr(
      "-------------------------\n"
      "Debugger stack trace...\n\n");
  DebuggerStackTrace* stack = Isolate::Current()->debugger()->StackTrace();
  intptr_t num_frames = stack->Length();
  for (intptr_t i = 0; i < num_frames; i++) {
    ActivationFrame* frame = stack->FrameAt(i);
    OS::PrintErr("#%04" Pd " %s", i, frame->ToCString());
  }

  OS::PrintErr(
      "-------------------------\n"
      "All frames...\n\n");
  StackFrameIterator iterator(ValidationPolicy::kDontValidateFrames,
                              Thread::Current(),
                              StackFrameIterator::kNoCrossThreadIteration);
  StackFrame* frame = iterator.NextFrame();
  intptr_t num = 0;
  while ((frame != nullptr)) {
    OS::PrintErr("#%04" Pd " %s\n", num++, frame->ToCString());
    frame = iterator.NextFrame();
  }
}

void ActivationFrame::VariableAt(intptr_t i,
                                 String* name,
                                 TokenPosition* declaration_token_pos,
                                 TokenPosition* visible_start_token_pos,
                                 TokenPosition* visible_end_token_pos,
                                 Object* value) {
  GetDescIndices();
  ASSERT(i < desc_indices_.length());
  intptr_t desc_index = desc_indices_[i];
  ASSERT(name != nullptr);

  *name = var_descriptors_.GetName(desc_index);

  UntaggedLocalVarDescriptors::VarInfo var_info;
  var_descriptors_.GetInfo(desc_index, &var_info);
  ASSERT(declaration_token_pos != nullptr);
  *declaration_token_pos = var_info.declaration_pos;
  ASSERT(visible_start_token_pos != nullptr);
  *visible_start_token_pos = var_info.begin_pos;
  ASSERT(visible_end_token_pos != nullptr);
  *visible_end_token_pos = var_info.end_pos;
  ASSERT(value != nullptr);
  const int8_t kind = var_info.kind();
  const auto variable_index = VariableIndex(var_info.index());
  if (kind == UntaggedLocalVarDescriptors::kStackVar) {
    *value = GetStackVar(variable_index);
  } else {
    ASSERT(kind == UntaggedLocalVarDescriptors::kContextVar);
    *value = GetContextVar(var_info.scope_id, variable_index.value());
  }
}

ObjectPtr ActivationFrame::GetContextVar(intptr_t var_ctx_level,
                                         intptr_t ctx_slot) {
  // The context level at the PC/token index of this activation frame.
  intptr_t frame_ctx_level = ContextLevel();

  return GetRelativeContextVar(var_ctx_level, ctx_slot, frame_ctx_level);
}

ObjectPtr ActivationFrame::GetRelativeContextVar(intptr_t var_ctx_level,
                                                 intptr_t ctx_slot,
                                                 intptr_t frame_ctx_level) {
  const Context& ctx = GetSavedCurrentContext();

  // It's possible that ctx was optimized out as no locals were captured by the
  // context. See issue #38182.
  if (ctx.IsNull()) {
    return Object::optimized_out().ptr();
  }

  intptr_t level_diff = frame_ctx_level - var_ctx_level;
  if (level_diff == 0) {
    if ((ctx_slot < 0) || (ctx_slot >= ctx.num_variables())) {
      PrintContextMismatchError(ctx_slot, frame_ctx_level, var_ctx_level);
    }
    ASSERT((ctx_slot >= 0) && (ctx_slot < ctx.num_variables()));
    return ctx.At(ctx_slot);
  } else if (level_diff > 0) {
    Context& var_ctx = Context::Handle(ctx.ptr());
    while (level_diff > 0 && !var_ctx.IsNull()) {
      level_diff--;
      var_ctx = var_ctx.parent();
    }
    if (var_ctx.IsNull() || (ctx_slot < 0) ||
        (ctx_slot >= var_ctx.num_variables())) {
      PrintContextMismatchError(ctx_slot, frame_ctx_level, var_ctx_level);
    }
    ASSERT(!var_ctx.IsNull());
    ASSERT((ctx_slot >= 0) && (ctx_slot < var_ctx.num_variables()));
    return var_ctx.At(ctx_slot);
  } else {
    PrintContextMismatchError(ctx_slot, frame_ctx_level, var_ctx_level);
    return Object::null();
  }
}

ArrayPtr ActivationFrame::GetLocalVariables() {
  GetDescIndices();
  intptr_t num_variables = desc_indices_.length();
  String& var_name = String::Handle();
  Object& value = Instance::Handle();
  const Array& list = Array::Handle(Array::New(2 * num_variables));
  for (intptr_t i = 0; i < num_variables; i++) {
    TokenPosition ignore = TokenPosition::kNoSource;
    VariableAt(i, &var_name, &ignore, &ignore, &ignore, &value);
    list.SetAt(2 * i, var_name);
    list.SetAt((2 * i) + 1, value);
  }
  return list.ptr();
}

ObjectPtr ActivationFrame::GetReceiver() {
  GetDescIndices();
  intptr_t num_variables = desc_indices_.length();
  String& var_name = String::Handle();
  Instance& value = Instance::Handle();
  for (intptr_t i = 0; i < num_variables; i++) {
    TokenPosition ignore = TokenPosition::kNoSource;
    VariableAt(i, &var_name, &ignore, &ignore, &ignore, &value);
    if (var_name.Equals(Symbols::This())) {
      return value.ptr();
    }
  }
  return Object::optimized_out().ptr();
}

static bool IsSyntheticVariableName(const String& var_name) {
  return (var_name.Length() >= 1) && (var_name.CharAt(0) == ':');
}

static bool IsPrivateVariableName(const String& var_name) {
  return (var_name.Length() >= 1) && (var_name.CharAt(0) == '_');
}

ObjectPtr ActivationFrame::EvaluateCompiledExpression(
    const ExternalTypedData& kernel_buffer,
    const Array& type_definitions,
    const Array& arguments,
    const TypeArguments& type_arguments) {
  auto thread = Thread::Current();
  auto zone = thread->zone();

  // The expression evaluation function will get all it's captured state passed
  // as parameters (with `this` being the exception). As a result, we treat the
  // expression evaluation function as either a top-level, static or instance
  // method.
  const auto& outermost =
      Function::Handle(zone, function().GetOutermostFunction());
  const auto& klass = Class::Handle(zone, outermost.Owner());
  const auto& library = Library::Handle(zone, klass.library());

  auto& receiver = Object::Handle(zone);
  if (!klass.IsTopLevel() && !outermost.is_static()) {
    receiver = GetReceiver();
    RELEASE_ASSERT(receiver.IsInstance() ||
                   receiver.ptr() == Object::optimized_out().ptr());
  }
  return Instance::EvaluateCompiledExpression(thread, receiver, library, klass,
                                              kernel_buffer, type_definitions,
                                              arguments, type_arguments);
}

TypeArgumentsPtr ActivationFrame::BuildParameters(
    const GrowableObjectArray& param_names,
    const GrowableObjectArray& param_values,
    const GrowableObjectArray& type_params_names,
    const GrowableObjectArray& type_params_bounds,
    const GrowableObjectArray& type_params_defaults) {
  GetDescIndices();
  bool type_arguments_available = false;
  String& name = String::Handle();
  String& existing_name = String::Handle();
  Object& value = Instance::Handle();
  TypeArguments& type_arguments = TypeArguments::Handle();
  intptr_t num_variables = desc_indices_.length();
  for (intptr_t i = 0; i < num_variables; i++) {
    TokenPosition ignore = TokenPosition::kNoSource;
    VariableAt(i, &name, &ignore, &ignore, &ignore, &value);
    if (name.Equals(Symbols::FunctionTypeArgumentsVar())) {
      type_arguments_available = true;
      type_arguments ^= value.ptr();
    } else if (
        !name.Equals(Symbols::This()) && !IsSyntheticVariableName(name) &&
        value.ptr() != Object::optimized_out().ptr() &&
        // TODO(57048): We currently pretend that uninitialized late variables
        // don't exist to avoid crashing when evaluating expressions that need
        // to access the late variables' values. The CFE team is working on a
        // better solution.
        value.ptr() != Object::sentinel().ptr()) {
      if (IsPrivateVariableName(name)) {
        name = Symbols::New(Thread::Current(), String::ScrubName(name));
      }
      bool conflict = false;
      for (intptr_t j = 0; j < param_names.Length(); j++) {
        existing_name ^= param_names.At(j);
        if (name.Equals(existing_name)) {
          conflict = true;
          break;
        }
      }
      // If local has the same name as a binding in the incoming scope, prefer
      // the one from the incoming scope, since it is logically a child scope
      // of the activation's current scope.
      if (!conflict) {
        param_names.Add(name);
        param_values.Add(value);
      }
    }
  }

  if ((function().IsGeneric() || function().HasGenericParent()) &&
      type_arguments_available) {
    intptr_t num_vars = function().NumTypeArguments();
    type_params_names.Grow(num_vars);
    type_params_names.SetLength(num_vars);
    type_params_bounds.Grow(num_vars);
    type_params_bounds.SetLength(num_vars);
    type_params_defaults.Grow(num_vars);
    type_params_defaults.SetLength(num_vars);
    AbstractType& bound = AbstractType::Handle();
    AbstractType& defaultType = AbstractType::Handle();
    TypeParameters& type_params = TypeParameters::Handle();
    Function& current = Function::Handle(function().ptr());
    intptr_t mapping_offset = num_vars;
    for (; !current.IsNull(); current = current.parent_function()) {
      type_params = current.type_parameters();
      if (type_params.IsNull()) continue;
      intptr_t size = current.NumTypeParameters();
      ASSERT(size > 0 && type_params.Length() == size);
      ASSERT(mapping_offset >= size);
      mapping_offset -= size;
      for (intptr_t j = 0; j < size; ++j) {
        name = type_params.NameAt(j);
        bound = type_params.BoundAt(j);
        defaultType = type_params.DefaultAt(j);
        // Write the names in backwards in terms of chain of functions.
        // But keep the order of names within the same function. so they
        // match up with the order of the types in 'type_arguments'.
        // Index:0 1 2 3 ...
        //       |Names in Grandparent| |Names in Parent| ..|Names in Child|
        type_params_names.SetAt(mapping_offset + j, name);
        type_params_bounds.SetAt(mapping_offset + j, bound);
        type_params_defaults.SetAt(mapping_offset + j, defaultType);
      }
    }
    if (!type_arguments.IsNull()) {
      if (type_arguments.Length() == 0) {
        for (intptr_t i = 0; i < num_vars; ++i) {
          type_arguments.SetTypeAt(i, Object::dynamic_type());
        }
      }
      ASSERT(type_arguments.Length() == num_vars);
    }
  }

  return type_arguments.ptr();
}

const char* ActivationFrame::ToCString() {
  if (function().IsNull()) {
    return Thread::Current()->zone()->PrintToString("[ Frame kind: %s]\n",
                                                    KindToCString(kind_));
  }
  const String& url = String::Handle(SourceUrl());
  intptr_t line = LineNumber();
  const char* func_name = function().ToFullyQualifiedCString();
  if (kind_ == kRegular) {
    return Thread::Current()->zone()->PrintToString(
        "[ Frame pc(0x%" Px ") fp(0x%" Px ") sp(0x%" Px
        ")\n"
        "\t%s offset = 0x%" Px
        "\n"
        "\tfunction = %s\n"
        "\turl = %s\n"
        "\tline = %" Pd
        "\n"
        "\tcontext = %s\n"
        "\tcontext level = %" Pd " ]\n",
        pc(), fp(), sp(), IsInterpreted() ? "bytecode" : "code",
        pc() - PayloadStart(), func_name, url.ToCString(), line,
        ctx_.ToCString(), ContextLevel());
  } else {
    return Thread::Current()->zone()->PrintToString(
        "[ Frame code function = %s\n"
        "\turl = %s\n"
        "\tline = %" Pd
        "\n"
        "\tcontext = %s]\n",
        func_name, url.ToCString(), line, ctx_.ToCString());
  }
}

void ActivationFrame::PrintToJSONObject(JSONObject* jsobj) {
  if (kind_ == kRegular) {
    PrintToJSONObjectRegular(jsobj);
  } else if (kind_ == kAsyncAwaiter) {
    PrintToJSONObjectAsyncAwaiter(jsobj);
  } else if (kind_ == kAsyncSuspensionMarker) {
    PrintToJSONObjectAsyncSuspensionMarker(jsobj);
  } else {
    UNIMPLEMENTED();
  }
}

void ActivationFrame::PrintToJSONObjectRegular(JSONObject* jsobj) {
  const Script& script = Script::Handle(SourceScript());
  jsobj->AddProperty("type", "Frame");
  jsobj->AddProperty("kind", KindToCString(kind_));
  const TokenPosition& pos = TokenPos();
  jsobj->AddLocation(script, pos);
  jsobj->AddProperty("function", function());
  if (IsInterpreted()) {
    jsobj->AddProperty("code", bytecode());
  } else {
    jsobj->AddProperty("code", code());
  }
  {
    JSONArray jsvars(jsobj, "vars");
    const int num_vars = NumLocalVariables();
    for (intptr_t v = 0; v < num_vars; v++) {
      String& var_name = String::Handle();
      Instance& var_value = Instance::Handle();
      TokenPosition declaration_token_pos = TokenPosition::kNoSource;
      TokenPosition visible_start_token_pos = TokenPosition::kNoSource;
      TokenPosition visible_end_token_pos = TokenPosition::kNoSource;
      VariableAt(v, &var_name, &declaration_token_pos, &visible_start_token_pos,
                 &visible_end_token_pos, &var_value);
      if (!IsSyntheticVariableName(var_name)) {
        JSONObject jsvar(&jsvars);
        jsvar.AddProperty("type", "BoundVariable");
        const char* scrubbed_var_name = String::ScrubName(var_name);
        jsvar.AddProperty("name", scrubbed_var_name);
        jsvar.AddProperty("value", var_value);
        // Where was the variable declared?
        jsvar.AddProperty("declarationTokenPos", declaration_token_pos);
        // When the variable becomes visible to the scope.
        jsvar.AddProperty("scopeStartTokenPos", visible_start_token_pos);
        // When the variable stops being visible to the scope.
        jsvar.AddProperty("scopeEndTokenPos", visible_end_token_pos);
      }
    }
  }
}

void ActivationFrame::PrintToJSONObjectAsyncAwaiter(JSONObject* jsobj) {
  jsobj->AddProperty("type", "Frame");
  jsobj->AddProperty("kind", KindToCString(kind_));
  const Script& script = Script::Handle(SourceScript());
  const TokenPosition& pos = TokenPos();
  jsobj->AddLocation(script, pos);
  jsobj->AddProperty("function", function());
  if (IsInterpreted()) {
    jsobj->AddProperty("code", bytecode());
  } else {
    jsobj->AddProperty("code", code());
  }
}

void ActivationFrame::PrintToJSONObjectAsyncSuspensionMarker(
    JSONObject* jsobj) {
  jsobj->AddProperty("type", "Frame");
  jsobj->AddProperty("kind", KindToCString(kind_));
  jsobj->AddProperty("marker", "AsynchronousSuspension");
}

static bool IsFunctionVisible(const Function& function) {
  return FLAG_show_invisible_frames || function.is_visible();
}

void DebuggerStackTrace::AddActivation(ActivationFrame* frame) {
  if (IsFunctionVisible(frame->function())) {
    trace_.Add(frame);
  }
}

void DebuggerStackTrace::AddAsyncSuspension() {
  // We might start asynchronous unwinding in one of the internal
  // dart:async functions which would make synchronous part of the
  // stack empty. This would not happen normally but might happen
  // with stress flags.
  if (trace_.is_empty() ||
      trace_.Last()->kind() != ActivationFrame::kAsyncSuspensionMarker) {
    trace_.Add(new ActivationFrame(ActivationFrame::kAsyncSuspensionMarker));
  }
}

void DebuggerStackTrace::AddAsyncAwaiterFrame(uword pc,
                                              const Function& function,
                                              const Object& code_or_bytecode,
                                              const Closure& closure) {
  trace_.Add(new ActivationFrame(pc, function, code_or_bytecode, closure));
}

const uint8_t kSafepointKind = UntaggedPcDescriptors::kIcCall |
                               UntaggedPcDescriptors::kUnoptStaticCall |
                               UntaggedPcDescriptors::kRuntimeCall;

CodeBreakpoint::CodeBreakpoint(const Code& code,
                               BreakpointLocation* breakpoint_location,
                               uword pc,
                               UntaggedPcDescriptors::Kind kind)
    : code_(code.ptr()),
      bytecode_(Bytecode::null()),
      pc_(pc),
      enabled_count_(0),
      next_(nullptr),
      breakpoint_kind_(kind),
      saved_value_(Code::null()),
      saved_opcode_(kMaxUint32) {
  ASSERT(!code.IsNull());
  ASSERT(pc_ != 0);
  ASSERT((breakpoint_kind_ & kSafepointKind) != 0);
  AddBreakpointLocation(breakpoint_location);
  ASSERT(breakpoint_location->token_pos().IsReal());
}

CodeBreakpoint::CodeBreakpoint(const Bytecode& bytecode,
                               BreakpointLocation* breakpoint_location,
                               uword pc)
    : code_(Code::null()),
      bytecode_(bytecode.ptr()),
      pc_(pc),
      enabled_count_(0),
      next_(nullptr),
      breakpoint_kind_(UntaggedPcDescriptors::kRuntimeCall),
      saved_value_(Code::null()),
      saved_opcode_(kMaxUint32) {
  ASSERT(!bytecode.IsNull());
  ASSERT(pc_ != 0);
  AddBreakpointLocation(breakpoint_location);
  ASSERT(breakpoint_location->token_pos().IsReal());
}

CodeBreakpoint::~CodeBreakpoint() {
  // Make sure we don't leave patched code behind.
  ASSERT(!IsEnabled());
// Poison the data so we catch use after free errors.
#ifdef DEBUG
  code_ = Code::null();
  bytecode_ = Bytecode::null();
  pc_ = 0ul;
  next_ = nullptr;
  breakpoint_kind_ = UntaggedPcDescriptors::kOther;
#endif
}

void CodeBreakpoint::Enable() {
  if (enabled_count_ == 0) {
    if (bytecode_ != Bytecode::null()) {
#if defined(DART_DYNAMIC_MODULES)
      ASSERT_EQUAL(saved_opcode_, kMaxUint32);
      saved_opcode_ =
          BytecodePatcher::AddBreakpointAt(pc_, Bytecode::Handle(bytecode_));
#else
      UNREACHABLE();
#endif
    } else {
      PatchCode();
    }
  }
  ++enabled_count_;
}

void CodeBreakpoint::Disable() {
  if (enabled_count_ == 1) {
    if (bytecode_ != Bytecode::null()) {
#if defined(DART_DYNAMIC_MODULES)
      BytecodePatcher::RemoveBreakpointAt(pc_, Bytecode::Handle(bytecode_),
                                          saved_opcode_);
      saved_opcode_ = kMaxUint32;
#else
      UNREACHABLE();
#endif
    } else {
      RestoreCode();
    }
  }
  --enabled_count_;
}

bool CodeBreakpoint::HasBreakpointLocation(
    BreakpointLocation* breakpoint_location) {
  for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
    if (breakpoint_locations_[i] == breakpoint_location) {
      return true;
    }
  }
  return false;
}

bool CodeBreakpoint::FindAndDeleteBreakpointLocation(
    BreakpointLocation* breakpoint_location) {
  for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
    if (breakpoint_locations_[i] == breakpoint_location) {
      breakpoint_locations_.EraseAt(i);
      return true;
    }
  }
  return false;
}

BreakpointLocation* CodeBreakpoint::FindBreakpointForDebugger(
    Debugger* debugger) {
  for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
    if (breakpoint_locations_[i]->debugger() == debugger) {
      return breakpoint_locations_[i];
    }
  }
  return nullptr;
}

GroupDebugger::GroupDebugger(IsolateGroup* isolate_group)
    : isolate_group_(isolate_group),
      code_breakpoints_lock_(new SafepointRwLock()),
      code_breakpoints_(nullptr),
      breakpoint_locations_lock_(new SafepointRwLock()),
      single_stepping_set_lock_(new RwLock()),
      needs_breakpoint_cleanup_(false) {}

GroupDebugger::~GroupDebugger() {
  while (code_breakpoints_ != nullptr) {
    CodeBreakpoint* cbpt = code_breakpoints_;
    code_breakpoints_ = code_breakpoints_->next();
    ASSERT(!cbpt->IsEnabled());
    delete cbpt;
  }
}

Debugger::Debugger(Isolate* isolate)
    : isolate_(isolate),
      next_id_(1),
      latent_locations_(nullptr),
      breakpoint_locations_(nullptr),
      resume_action_(kContinue),
      resume_frame_index_(-1),
      post_deopt_frame_index_(-1),
      ignore_breakpoints_(false),
      pause_event_(nullptr),
      stack_trace_(nullptr),
      async_awaiter_stack_trace_(nullptr),
      stepping_fp_(0),
      last_stepping_fp_(0),
      last_stepping_pos_(TokenPosition::kNoSource),
      skip_next_step_(false),
      exc_pause_info_(kNoPauseOnExceptions) {}

Debugger::~Debugger() {
  ASSERT(!IsPaused());
  ASSERT(latent_locations_ == nullptr);
  ASSERT(breakpoint_locations_ == nullptr);
  ASSERT(stack_trace_ == nullptr);
  ASSERT(async_awaiter_stack_trace_ == nullptr);
}

void Debugger::Shutdown() {
  // TODO(johnmccutchan): Do not create a debugger for isolates that don't need
  // them. Then, assert here that isolate_ is not one of those isolates.
  if (Isolate::IsSystemIsolate(isolate_)) {
    return;
  }
  {
    SafepointWriteRwLocker sl(Thread::Current(),
                              group_debugger()->breakpoint_locations_lock());
    while (breakpoint_locations_ != nullptr) {
      BreakpointLocation* loc = breakpoint_locations_;
      group_debugger()->UnlinkCodeBreakpoints(loc);
      group_debugger()->UnregisterBreakpointLocation(loc);
      breakpoint_locations_ = breakpoint_locations_->next();
      delete loc;
    }
    while (latent_locations_ != nullptr) {
      BreakpointLocation* loc = latent_locations_;
      group_debugger()->UnlinkCodeBreakpoints(loc);
      group_debugger()->UnregisterBreakpointLocation(loc);
      latent_locations_ = latent_locations_->next();
      delete loc;
    }
  }
  if (NeedsIsolateEvents()) {
    ServiceEvent event(isolate_, ServiceEvent::kIsolateExit);
    InvokeEventHandler(&event);
  }
}

static ActivationFrame* TopDartFrame();
static bool IsAtAsyncJump(ActivationFrame* top_frame);

bool Debugger::SetupStepOverAsyncSuspension(const char** error) {
  ActivationFrame* top_frame = TopDartFrame();
  if (!IsAtAsyncJump(top_frame)) {
    // Not at an async operation.
    if (error != nullptr) {
      *error = "Isolate must be paused at an async suspension point";
    }
    return false;
  }
  ASSERT(top_frame->function().IsAsyncFunction() ||
         top_frame->function().IsAsyncGenerator());
  const auto& function_data =
      Object::Handle(top_frame->GetSuspendableFunctionData());
  SetBreakpointAtResumption(function_data);
  return true;
}

static bool CanRewindFrame(intptr_t frame_index, const char** error);

bool Debugger::SetResumeAction(ResumeAction action,
                               intptr_t frame_index,
                               const char** error) {
  if (error != nullptr) {
    *error = nullptr;
  }
  resume_frame_index_ = -1;
  switch (action) {
    case kStepInto:
    case kStepOver:
    case kStepOut:
    case kContinue:
      set_resume_action(action);
      return true;
    case kStepRewind:
      if (!CanRewindFrame(frame_index, error)) {
        return false;
      }
      set_resume_action(kStepRewind);
      resume_frame_index_ = frame_index;
      return true;
    case kStepOverAsyncSuspension:
      return SetupStepOverAsyncSuspension(error);
    default:
      UNREACHABLE();
      return false;
  }
}

// Deoptimize all functions in the isolate.
// TODO(hausner): Actually we only need to deoptimize those functions
// that inline the function that contains the newly created breakpoint.
// We currently don't have this info so we deoptimize all functions.
void Debugger::DeoptimizeWorld() {
#if defined(DART_PRECOMPILED_RUNTIME)
  UNREACHABLE();
#else
  if (FLAG_trace_deoptimization) {
    THR_Print("Deopt for debugger\n");
  }
  isolate_->group()->set_has_attempted_stepping(true);

  DeoptimizeFunctionsOnStack();

  // Iterate over all classes, deoptimize functions.
  // TODO(hausner): Could possibly be combined with RemoveOptimizedCode()
  const ClassTable& class_table = *isolate_->group()->class_table();
  auto thread = Thread::Current();
  auto isolate_group = thread->isolate_group();
  auto zone = thread->zone();
  CallSiteResetter resetter(zone);
  Class& cls = Class::Handle(zone);
  Array& functions = Array::Handle(zone);
  Function& function = Function::Handle(zone);
  Code& code = Code::Handle(zone);

  const intptr_t num_classes = class_table.NumCids();
  const intptr_t num_tlc_classes = class_table.NumTopLevelCids();
  SafepointWriteRwLocker ml(thread, isolate_group->program_lock());
  for (intptr_t i = 1; i < num_classes + num_tlc_classes; i++) {
    const intptr_t cid =
        i < num_classes ? i : ClassTable::CidFromTopLevelIndex(i - num_classes);
    if (class_table.HasValidClassAt(cid)) {
      cls = class_table.At(cid);

      // Disable optimized functions.
      functions = cls.functions();
      if (!functions.IsNull()) {
        intptr_t num_functions = functions.Length();
        for (intptr_t pos = 0; pos < num_functions; pos++) {
          function ^= functions.At(pos);
          ASSERT(!function.IsNull());
          // Force-optimized functions don't have unoptimized code and can't
          // deoptimize. Their optimized codes are still valid.
          if (!function.ForceOptimize()) {
            if (function.HasOptimizedCode()) {
              function.SwitchToUnoptimizedCode();
            }
            code = function.unoptimized_code();
            if (!code.IsNull()) {
              resetter.ResetSwitchableCalls(code);
            }
          }
          // Also disable any optimized implicit closure functions.
          if (function.HasImplicitClosureFunction()) {
            function = function.ImplicitClosureFunction();
            if (!function.ForceOptimize()) {
              if (function.HasOptimizedCode()) {
                function.SwitchToUnoptimizedCode();
              }
              code = function.unoptimized_code();
              if (!code.IsNull()) {
                resetter.ResetSwitchableCalls(code);
              }
            }
          }
        }
      }
    }
  }

  // Disable optimized closure functions.
  ClosureFunctionsCache::ForAllClosureFunctions([&](const Function& function) {
    if (!function.ForceOptimize()) {
      if (function.HasOptimizedCode()) {
        function.SwitchToUnoptimizedCode();
      }
      code = function.unoptimized_code();
      if (!code.IsNull()) {
        resetter.ResetSwitchableCalls(code);
      }
    }
    return true;  // Continue iteration.
  });
#endif  // defined(DART_PRECOMPILED_RUNTIME)
}

void Debugger::RunWithStoppedDeoptimizedWorld(std::function<void()> fun) {
#if !defined(DART_PRECOMPILED_RUNTIME)
  // RELOAD_OPERATION_SCOPE is used here because is is guaranteed that
  // isolates at reload safepoints hold no safepoint locks.
  RELOAD_OPERATION_SCOPE(Thread::Current());
  group_debugger()->isolate_group()->RunWithStoppedMutators([&]() {
    DeoptimizeWorld();
    fun();
  });
#endif
}

void Debugger::NotifySingleStepping(bool value) {
  RELEASE_ASSERT(isolate_->mutator_thread() != nullptr);
  if (value) {
    // Setting breakpoint requires unoptimized code, make sure we stop all
    // isolates to prevent racing reoptimization.
    RunWithStoppedDeoptimizedWorld([&] {
      isolate_->mutator_thread()->set_single_step(value);
      // Ensure other isolates in the isolate group keep
      // unoptimized code unoptimized, won't attempt to optimize it.
      group_debugger()->RegisterSingleSteppingDebugger(Thread::Current(), this);
    });
  } else {
    isolate_->mutator_thread()->set_single_step(value);
    group_debugger()->UnregisterSingleSteppingDebugger(Thread::Current(), this);
  }
}

static ActivationFrame* CollectDartFrame(uword pc,
                                         StackFrame* frame,
                                         const Function& function,
                                         const Object& code_or_bytecode,
                                         const Array& deopt_frame,
                                         intptr_t deopt_frame_offset) {
  ASSERT((code_or_bytecode.IsCode() &&
          Code::Cast(code_or_bytecode).ContainsInstructionAt(pc)) ||
         (code_or_bytecode.IsBytecode() &&
          Bytecode::Cast(code_or_bytecode).ContainsInstructionAt(pc)));
  ActivationFrame* activation =
      new ActivationFrame(pc, frame->fp(), frame->sp(), function,
                          code_or_bytecode, deopt_frame, deopt_frame_offset);
  if (FLAG_trace_debugger_stacktrace) {
    const Context& ctx = activation->GetSavedCurrentContext();
    OS::PrintErr("\tUsing saved context: %s\n", ctx.ToCString());
    OS::PrintErr("\tLine number: %" Pd "\n", activation->LineNumber());
  }
  return activation;
}

#if !defined(DART_PRECOMPILED_RUNTIME)
static ArrayPtr DeoptimizeToArray(Thread* thread,
                                  StackFrame* frame,
                                  const Code& code) {
  ASSERT(code.is_optimized() && !code.is_force_optimized());
  // Create the DeoptContext for this deoptimization.
  DeoptContext* deopt_context =
      new DeoptContext(frame, code, DeoptContext::kDestIsAllocated, nullptr,
                       nullptr, true, false /* deoptimizing_code */);
  thread->set_deopt_context(deopt_context);

  deopt_context->FillDestFrame();
  deopt_context->MaterializeDeferredObjects();
  const Array& dest_frame =
      Array::Handle(thread->zone(), deopt_context->DestFrameAsArray());

  thread->set_deopt_context(nullptr);
  delete deopt_context;

  return dest_frame.ptr();
}
#endif  // !defined(DART_PRECOMPILED_RUNTIME)

DebuggerStackTrace* DebuggerStackTrace::Collect() {
  Thread* thread = Thread::Current();
  Zone* zone = thread->zone();

  auto& function = Function::Handle(zone);
  auto& code = Code::Handle(zone);
  auto& bytecode = Bytecode::Handle(zone);
  DebuggerStackTrace* stack_trace = new DebuggerStackTrace(8);

  StackFrameIterator iterator(ValidationPolicy::kDontValidateFrames, thread,
                              StackFrameIterator::kNoCrossThreadIteration);
  for (StackFrame* frame = iterator.NextFrame(); frame != nullptr;
       frame = iterator.NextFrame()) {
    ASSERT(frame->IsValid());
    if (FLAG_trace_debugger_stacktrace) {
      OS::PrintErr("CollectStackTrace: visiting frame:\n\t%s\n",
                   frame->ToCString());
    }
    if (frame->IsDartFrame()) {
      if (frame->is_interpreted()) {
        function = frame->LookupDartFunction();
        bytecode = frame->LookupDartBytecode();
        stack_trace->AppendBytecodeFrame(frame, function, bytecode);
      } else {
        code = frame->LookupDartCode();
        stack_trace->AppendCodeFrames(frame, code);
      }
    }
  }
  return stack_trace;
}

// Appends at least one stack frame. Multiple frames will be appended
// if |code| at the frame's pc contains inlined functions.
void DebuggerStackTrace::AppendCodeFrames(StackFrame* frame, const Code& code) {
  auto& function = Function::Handle(zone_, code.function());
#if !defined(DART_PRECOMPILED_RUNTIME)
  if (code.is_optimized()) {
    if (code.is_force_optimized()) {
      if (FLAG_trace_debugger_stacktrace) {
        ASSERT(!function.IsNull());
        OS::PrintErr(
            "CollectStackTrace: skipping force-optimized function: %s\n",
            function.ToFullyQualifiedCString());
      }
      return;  // Skip frame of force-optimized (and non-debuggable) function.
    }
    // TODO(rmacnak): Use CodeSourceMap
    deopt_frame_ = DeoptimizeToArray(thread_, frame, code);
    for (InlinedFunctionsIterator it(code, frame->pc()); !it.Done();
         it.Advance()) {
      inlined_code_ = it.code();
      function = it.function();
      if (FLAG_trace_debugger_stacktrace) {
        ASSERT(!function.IsNull());
        OS::PrintErr("CollectStackTrace: visiting inlined function: %s\n",
                     function.ToFullyQualifiedCString());
      }
      intptr_t deopt_frame_offset = it.GetDeoptFpOffset();
      AddActivation(CollectDartFrame(it.pc(), frame, function, inlined_code_,
                                     deopt_frame_, deopt_frame_offset));
    }
    return;
  }
#endif  // !defined(DART_PRECOMPILED_RUNTIME)
  AddActivation(CollectDartFrame(frame->pc(), frame, function, code,
                                 Object::null_array(), 0));
}

// Appends one interpreted stack frame.
void DebuggerStackTrace::AppendBytecodeFrame(StackFrame* frame,
                                             const Function& function,
                                             const Bytecode& bytecode) {
  AddActivation(CollectDartFrame(frame->pc(), frame, function, bytecode,
                                 Object::null_array(), 0));
}

DebuggerStackTrace* DebuggerStackTrace::CollectAsyncAwaiters() {
  Thread* thread = Thread::Current();
  Zone* zone = thread->zone();

  Function& function = Function::Handle(zone);

  constexpr intptr_t kDefaultStackAllocation = 8;
  auto stack_trace = new DebuggerStackTrace(kDefaultStackAllocation);

  bool has_async = false;
  bool has_async_catch_error = false;
  StackTraceUtils::CollectFrames(
      thread, /*skip_frames=*/0,
      [&](const StackTraceUtils::Frame& frame) {
        if (frame.code.IsNull() && frame.bytecode.IsNull()) {
          return;
        }

        if (frame.frame != nullptr) {  // Synchronous portion of the stack.
          if (!frame.bytecode.IsNull()) {
            function = frame.frame->LookupDartFunction();
            stack_trace->AppendBytecodeFrame(frame.frame, function,
                                             frame.bytecode);
          } else {
            stack_trace->AppendCodeFrames(frame.frame, frame.code);
          }
        } else {
          has_async = true;

          if (frame.code.ptr() == StubCode::AsynchronousGapMarker().ptr()) {
            stack_trace->AddAsyncSuspension();
            return;
          }

          if (!frame.bytecode.IsNull()) {
            function = frame.bytecode.function();
            ASSERT(!function.IsNull());
            if (!function.is_visible()) {
              return;
            }
            const uword absolute_pc =
                frame.bytecode.PayloadStart() + frame.pc_offset;
            stack_trace->AddAsyncAwaiterFrame(absolute_pc, function,
                                              frame.bytecode, frame.closure);
          } else {
            function = frame.code.function();
            if (!function.is_visible()) {
              return;
            }
            const uword absolute_pc =
                frame.code.PayloadStart() + frame.pc_offset;
            stack_trace->AddAsyncAwaiterFrame(absolute_pc, function, frame.code,
                                              frame.closure);
          }
        }
      },
      &has_async_catch_error);

  // If the entire stack is sync, return no (async) trace.
  if (!has_async) {
    return nullptr;
  }

  stack_trace->set_has_async_catch_error(has_async_catch_error);

  return stack_trace;
}

static ActivationFrame* TopDartFrame() {
  StackFrameIterator iterator(ValidationPolicy::kDontValidateFrames,
                              Thread::Current(),
                              StackFrameIterator::kNoCrossThreadIteration);
  StackFrame* frame;
  while (true) {
    frame = iterator.NextFrame();
    RELEASE_ASSERT(frame != nullptr);
    if (!frame->IsDartFrame()) {
      continue;
    }
    auto& function = Function::Handle();
    auto& code_or_bytecode = Object::Handle();
    if (frame->is_interpreted()) {
      code_or_bytecode = frame->LookupDartBytecode();
      function = frame->LookupDartFunction();
    } else {
      code_or_bytecode = frame->LookupDartCode();
      function = Code::Cast(code_or_bytecode).function();
    }
    ActivationFrame* activation =
        new ActivationFrame(frame->pc(), frame->fp(), frame->sp(), function,
                            code_or_bytecode, Object::null_array(), 0);
    return activation;
  }
}

DebuggerStackTrace* Debugger::StackTrace() {
  return (stack_trace_ != nullptr) ? stack_trace_
                                   : DebuggerStackTrace::Collect();
}

DebuggerStackTrace* Debugger::AsyncAwaiterStackTrace() {
  return (async_awaiter_stack_trace_ != nullptr)
             ? async_awaiter_stack_trace_
             : DebuggerStackTrace::CollectAsyncAwaiters();
}

DebuggerStackTrace* DebuggerStackTrace::From(const class StackTrace& ex_trace) {
  DebuggerStackTrace* stack_trace = new DebuggerStackTrace(8);
  Function& function = Function::Handle();
  Object& code_object = Object::Handle();
  Code& code = Code::Handle();

  const uword fp = 0;
  const uword sp = 0;
  const Array& deopt_frame = Array::Handle();
  const intptr_t deopt_frame_offset = -1;

  for (intptr_t i = 0; i < ex_trace.Length(); i++) {
    code_object = ex_trace.CodeAtFrame(i);
    // Pre-allocated StackTraces may include empty slots, either (a) to indicate
    // where frames were omitted in the case a stack has more frames than the
    // pre-allocated trace (such as a stack overflow) or (b) because a stack has
    // fewer frames that the pre-allocated trace (such as memory exhaustion with
    // a shallow stack).
    if (!code_object.IsNull()) {
      code ^= code_object.ptr();
      ASSERT(code.IsFunctionCode());
      function = code.function();
      if (function.is_visible()) {
        ASSERT(function.ptr() == code.function());
        uword pc = code.PayloadStart() + ex_trace.PcOffsetAtFrame(i);
        if (code.is_optimized() && ex_trace.expand_inlined()) {
          // Traverse inlined frames.
          for (InlinedFunctionsIterator it(code, pc); !it.Done();
               it.Advance()) {
            function = it.function();
            code = it.code();
            ASSERT(function.ptr() == code.function());
            uword pc = it.pc();
            ASSERT(pc != 0);
            ASSERT(code.PayloadStart() <= pc);
            ASSERT(pc < (code.PayloadStart() + code.Size()));

            ActivationFrame* activation = new ActivationFrame(
                pc, fp, sp, function, code, deopt_frame, deopt_frame_offset);
            stack_trace->AddActivation(activation);
          }
        } else {
          ActivationFrame* activation = new ActivationFrame(
              pc, fp, sp, function, code, deopt_frame, deopt_frame_offset);
          stack_trace->AddActivation(activation);
        }
      }
    }
  }
  return stack_trace;
}

void Debugger::SetExceptionPauseInfo(Dart_ExceptionPauseInfo pause_info) {
  ASSERT((pause_info == kNoPauseOnExceptions) ||
         (pause_info == kPauseOnUnhandledExceptions) ||
         (pause_info == kPauseOnAllExceptions));
  exc_pause_info_ = pause_info;
}

Dart_ExceptionPauseInfo Debugger::GetExceptionPauseInfo() const {
  return exc_pause_info_;
}

bool Debugger::ShouldPauseOnException(DebuggerStackTrace* stack_trace,
                                      const Instance& exception) {
  if (exc_pause_info_ == kNoPauseOnExceptions) {
    return false;
  }
  if (exc_pause_info_ == kPauseOnAllExceptions) {
    return true;
  }
  ASSERT(exc_pause_info_ == kPauseOnUnhandledExceptions);
  // There might be no Dart stack if we hit an exception in the runtime, most
  // likely OutOfMemory.
  if (stack_trace->Length() == 0) {
    return false;
  }
  // Exceptions coming from invalid token positions should be skipped
  ActivationFrame* top_frame = stack_trace->FrameAt(0);
  if (!top_frame->TokenPos().IsReal() && top_frame->TryIndex() != -1) {
    return false;
  }
  ActivationFrame* handler_frame = stack_trace->GetHandlerFrame(exception);
  if (handler_frame == nullptr) {
    // Did not find an exception handler that catches this exception.
    // Note that this check is not precise, since we can't check
    // uninstantiated types, i.e. types containing type parameters.
    // Thus, we may report an exception as unhandled when in fact
    // it will be caught once we unwind the stack.
    return !stack_trace->has_async_catch_error();
  }

  auto& handler_function = Function::Handle(handler_frame->function().ptr());

  // If handler_frame's function is annotated with
  // @pragma('vm:notify-debugger-on-exception'), we specifically want to notify
  // the debugger of this otherwise ignored exception.
  if (!handler_function.IsNull() &&
      Library::FindPragma(Thread::Current(), /*only_core=*/false,
                          handler_function,
                          Symbols::vm_notify_debugger_on_exception())) {
    return true;
  }
  return false;
}

void Debugger::PauseException(const Instance& exc) {
  if (FLAG_stress_async_stacks) {
    DebuggerStackTrace::CollectAsyncAwaiters();
  }
  // We ignore this exception event when the VM is executing code invoked
  // by the debugger to evaluate variables values, when we see a nested
  // breakpoint or exception event, or if the debugger is not
  // interested in exception events.
  if (ignore_breakpoints_ || IsPaused() ||
      (exc_pause_info_ == kNoPauseOnExceptions)) {
    return;
  }
  DebuggerStackTrace* async_awaiter_stack_trace =
      DebuggerStackTrace::CollectAsyncAwaiters();
  DebuggerStackTrace* stack_trace = DebuggerStackTrace::Collect();
  if (async_awaiter_stack_trace != nullptr) {
    if (!ShouldPauseOnException(async_awaiter_stack_trace, exc)) {
      return;
    }
  } else {
    if (!ShouldPauseOnException(stack_trace, exc)) {
      return;
    }
  }
  ServiceEvent event(isolate_, ServiceEvent::kPauseException);
  event.set_exception(&exc);
  if (stack_trace->Length() > 0) {
    event.set_top_frame(stack_trace->FrameAt(0));
  }
  CacheStackTraces(stack_trace, async_awaiter_stack_trace);
  Pause(&event);
  HandleSteppingRequest();  // we may get a rewind request
  ClearCachedStackTraces();
}

// Helper that refines the resolved token pos.
//
// If |requested_column| is |-1|, then |exact_token_pos| must be
// |TokenPosition::kNoSource|.
static void RefineBreakpointPos(const Script& script,
                                TokenPosition pos,
                                TokenPosition next_closest_token_position,
                                TokenPosition requested_token_pos,
                                TokenPosition last_token_pos,
                                intptr_t requested_column,
                                TokenPosition exact_token_pos,
                                TokenPosition* best_fit_pos,
                                intptr_t* best_column,
                                intptr_t* best_line) {
  ASSERT(
      (requested_column == -1 && exact_token_pos == TokenPosition::kNoSource) ||
      requested_column > -1);

  intptr_t token_start_column = -1;
  intptr_t token_line = -1;
  if (requested_column >= 0) {
    TokenPosition ignored = TokenPosition::kNoSource;
    TokenPosition end_of_line_pos = TokenPosition::kNoSource;
    script.GetTokenLocation(pos, &token_line, &token_start_column);
    script.TokenRangeAtLine(token_line, &ignored, &end_of_line_pos);

    TokenPosition token_end_pos = TokenPosition::Min(
        TokenPosition::Deserialize(next_closest_token_position.Pos() - 1),
        end_of_line_pos);

    if ((token_end_pos.IsReal() && exact_token_pos.IsReal() &&
         (token_end_pos < exact_token_pos)) ||
        (token_start_column > *best_column)) {
      // We prefer the token with the lowest column number compatible with the
      // requested column. The current token under consideration either ends
      // before the requested column, and is thus incompatible with it, or
      // has a higher column number than the best token we've found so far, so
      // we reject the current token under consideration.
      return;
    }
  }

  // Prefer the lowest (first) token pos.
  if (pos < *best_fit_pos) {
    *best_fit_pos = pos;
    *best_line = token_line;
    *best_column = token_start_column;
  }
}

// Returns the best fit token position for a breakpoint.
//
// Takes a range of tokens [requested_token_pos, last_token_pos] and
// an optional column (requested_column).  The range of tokens usually
// represents one line of the program text, but can represent a larger
// range on recursive calls.
//
// If |requested_column| is |-1|, then |exact_token_pos| must be
// |TokenPosition::kNoSource|.
//
// The best fit is found in two passes.
//
// The first pass finds a candidate token which:
//
//   - is a safepoint,
//   - has the lowest column number compatible with the requested column
//     if a column has been specified,
// and:
//   - has the lowest token position number which satisfies the above.
//
// When we consider a column number, we look for the token which
// intersects the desired column.  For example:
//
//          1         2         3
// 12345678901234567890         0
//
//   var x = function(function(y));
//              ^
//
// If we request a breakpoint at column 14, the lowest column number
// compatible with that would for column 11 (beginning of the
// 'function' token) in the example above.
//
// Once this candidate token from the first pass is found, we then
// have a second pass which considers only those tokens on the same
// line as the candidate token.
//
// The second pass finds a best fit token which:
//
//   - is a safepoint,
//   - has the same column number as the candidate token (perhaps
//     more than one token has the same column number),
// and:
//   - has the lowest code address in the generated code.
//
// We prefer the lowest compiled code address, because this tends to
// select the first subexpression on a line.  For example in a line
// with nested function calls f(g(x)), the call to g() will have a
// lower compiled code address than the call to f().
//
// If no best fit token can be found, the search is expanded,
// searching through the rest of the current function by calling this
// function recursively.
//
// TODO(turnidge): Given that we usually call this function with a
// token range restricted to a single line, this could be a one-pass
// algorithm, which would be simpler.  I believe that it only needs
// two passes to support the recursive try-the-whole-function case.
// Rewrite this later, once there are more tests in place.
static TokenPosition ResolveBreakpointPos(const Function& func,
                                          TokenPosition requested_token_pos,
                                          TokenPosition last_token_pos,
                                          intptr_t requested_column,
                                          TokenPosition exact_token_pos) {
  ASSERT(!func.HasOptimizedCode());
  ASSERT(
      (requested_column == -1 && exact_token_pos == TokenPosition::kNoSource) ||
      requested_column > -1);

  requested_token_pos =
      TokenPosition::Max(requested_token_pos, func.token_pos());
  last_token_pos = TokenPosition::Min(last_token_pos, func.end_token_pos());

  Zone* zone = Thread::Current()->zone();
  Script& script = Script::Handle(zone, func.script());
  PcDescriptors& desc = PcDescriptors::Handle(zone);
#if defined(DART_DYNAMIC_MODULES)
  auto& bytecode = Bytecode::Handle(zone);
#endif
  if (func.HasBytecode()) {
#if defined(DART_DYNAMIC_MODULES)
    bytecode = func.GetBytecode();
    ASSERT(!bytecode.IsNull());
    if (!bytecode.HasSourcePositions()) {
      return TokenPosition::kNoSource;
    }
#else
    UNREACHABLE();
#endif
  } else {
    ASSERT(func.HasCode());
    const auto& code = Code::Handle(zone, func.unoptimized_code());
    ASSERT(!code.IsNull());
    desc = code.pc_descriptors();
  }

  // First pass: find the safe point which is closest to the beginning
  // of the given token range.
  TokenPosition best_fit_pos = TokenPosition::kMaxSource;
  intptr_t best_column = INT_MAX;
  intptr_t best_line = INT_MAX;

  if (func.HasBytecode()) {
#if defined(DART_DYNAMIC_MODULES)
    // Only compiled code has synthetic token positions.
    ASSERT(!requested_token_pos.IsSynthetic());
    bytecode::BytecodeSourcePositionsIterator iter(zone, bytecode);
    while (iter.MoveNext()) {
      const TokenPosition& pos = iter.TokenPos();
      if (!pos.IsWithin(requested_token_pos, last_token_pos)) {
        // Token is not in the target range.
        continue;
      }
      TokenPosition next_closest_token_position = TokenPosition::kMaxSource;
      if (requested_column >= 0) {
        // Find next closest emitted source position.
        bytecode::BytecodeSourcePositionsIterator iter2(zone, bytecode);
        while (iter2.MoveNext()) {
          const TokenPosition& next = iter2.TokenPos();
          if (!next.IsReal()) continue;
          if ((pos < next) && (next < next_closest_token_position)) {
            next_closest_token_position = next;
          }
        }
      }
      RefineBreakpointPos(script, pos, next_closest_token_position,
                          requested_token_pos, last_token_pos, requested_column,
                          exact_token_pos, &best_fit_pos, &best_column,
                          &best_line);
    }
#endif
  } else {
    PcDescriptors::Iterator iter(desc, kSafepointKind);
    while (iter.MoveNext()) {
      const TokenPosition& pos = iter.TokenPos();
      if (pos.IsSynthetic() && pos == requested_token_pos) {
        // if there's a safepoint for a synthetic function start and the start
        // was requested, we're done.
        return pos;
      }
      if (!pos.IsWithin(requested_token_pos, last_token_pos)) {
        // Token is not in the target range.
        continue;
      }
      TokenPosition next_closest_token_position = TokenPosition::kMaxSource;
      if (requested_column >= 0) {
        // Find next closest safepoint
        PcDescriptors::Iterator iter2(desc, kSafepointKind);
        while (iter2.MoveNext()) {
          const TokenPosition& next = iter2.TokenPos();
          if (!next.IsReal()) continue;
          if ((pos < next) && (next < next_closest_token_position)) {
            next_closest_token_position = next;
          }
        }
      }
      RefineBreakpointPos(script, pos, next_closest_token_position,
                          requested_token_pos, last_token_pos, requested_column,
                          exact_token_pos, &best_fit_pos, &best_column,
                          &best_line);
    }
  }

  // Second pass (if we found a safe point in the first pass).  Find
  // the token on the line which is at the best fit column (if column
  // was specified) and has the lowest code address.
  if (best_fit_pos != TokenPosition::kMaxSource) {
    ASSERT(best_fit_pos.IsReal());
    const Script& script = Script::Handle(zone, func.script());
    const TokenPosition begin_pos = best_fit_pos;

    TokenPosition end_of_line_pos = TokenPosition::kNoSource;
    if (best_line < 0) {
      script.GetTokenLocation(begin_pos, &best_line);
    }
    ASSERT(best_line > 0);
    TokenPosition ignored = TokenPosition::kNoSource;
    script.TokenRangeAtLine(best_line, &ignored, &end_of_line_pos);
    end_of_line_pos = TokenPosition::Max(end_of_line_pos, begin_pos);

    uword lowest_pc_offset = kUwordMax;
    if (func.HasBytecode()) {
#if defined(DART_DYNAMIC_MODULES)
      bytecode::BytecodeSourcePositionsIterator iter(zone, bytecode);
      while (iter.MoveNext()) {
        const TokenPosition& pos = iter.TokenPos();
        uword pc_offset = iter.PcOffset();
        if (requested_column >= 0) {
          if (pos != best_fit_pos) {
            // Not an match for the requested column.
            continue;
          }
        } else if (!pos.IsWithin(begin_pos, end_of_line_pos)) {
          // Token is not on same line as best fit.
          continue;
        }
        // Prefer the lowest pc offset.
        if (pc_offset < lowest_pc_offset) {
          lowest_pc_offset = pc_offset;
          best_fit_pos = pos;
        }
      }
#endif
    } else {
      PcDescriptors::Iterator iter(desc, kSafepointKind);
      while (iter.MoveNext()) {
        const TokenPosition& pos = iter.TokenPos();
        if (requested_column >= 0) {
          if (pos != best_fit_pos) {
            // Not an match for the requested column.
            continue;
          }
        } else if (!pos.IsWithin(begin_pos, end_of_line_pos)) {
          // Token is not on same line as best fit.
          continue;
        }

        // Prefer the lowest pc offset.
        if (iter.PcOffset() < lowest_pc_offset) {
          lowest_pc_offset = iter.PcOffset();
          best_fit_pos = pos;
        }
      }
    }
    return best_fit_pos;
  }

  return TokenPosition::kNoSource;
}

bool BreakpointLocation::EnsureIsResolved(const Function& target_function,
                                          TokenPosition exact_token_pos) {
  if (IsResolved()) {
    return true;
  }

  // Resolve source breakpoint in the newly compiled function.
  TokenPosition resolved_pos =
      ResolveBreakpointPos(target_function, token_pos(), end_token_pos(),
                           requested_column_number(), exact_token_pos);
  if (!resolved_pos.IsDebugPause()) {
    if (FLAG_verbose_debug) {
      OS::PrintErr("Failed resolving breakpoint for function '%s'\n",
                   target_function.ToFullyQualifiedCString());
    }
    return false;
  }
  TokenPosition requested_pos = token_pos();
  TokenPosition requested_end_pos = end_token_pos();
  SetResolved(target_function, resolved_pos);
  Breakpoint* breakpoint = breakpoints();
  while (breakpoint != nullptr) {
    if (FLAG_verbose_debug) {
      OS::PrintErr("Resolved breakpoint %" Pd
                   " to pos %s, function '%s' (requested range %s-%s, "
                   "requested col %" Pd ")\n",
                   breakpoint->id(), token_pos().ToCString(),
                   target_function.ToFullyQualifiedCString(),
                   requested_pos.ToCString(), requested_end_pos.ToCString(),
                   requested_column_number());
    }
    debugger()->SendBreakpointEvent(ServiceEvent::kBreakpointResolved,
                                    breakpoint);
    breakpoint = breakpoint->next();
  }

  return true;
}

void GroupDebugger::MakeCodeBreakpointAtUnsafe(Thread* thread,
                                               const Function& func,
                                               BreakpointLocation* loc) {
  DEBUG_ASSERT(thread->IsInStoppedMutatorsScope() ||
               code_breakpoints_lock()->IsCurrentThreadWriter());

  ASSERT(loc->token_pos().IsReal());
  ASSERT((loc != nullptr) && loc->IsResolved());
  auto* zone = thread->zone();
  auto& code = Code::Handle(zone);
  auto& bytecode = Bytecode::Handle(zone);
  uword start = 0;
  uword lowest_pc_offset = kUwordMax;
  UntaggedPcDescriptors::Kind lowest_kind = UntaggedPcDescriptors::kAnyKind;
  // Find the safe point with the lowest compiled code address
  // that maps to the token position of the source breakpoint.
  if (func.HasBytecode()) {
#if defined(DART_DYNAMIC_MODULES)
    bytecode = func.GetBytecode();
    ASSERT(!bytecode.IsNull());
    if (!bytecode.HasSourcePositions()) {
      return;
    }
    start = bytecode.PayloadStart();
    bytecode::BytecodeSourcePositionsIterator iter(zone, bytecode);
    while (iter.MoveNext()) {
      if (iter.TokenPos() == loc->token_pos_) {
        // Breakpoints are set and located using the address of the instruction
        // following the breakpoint instruction, since frames contain
        // return addresses.
        const uword pc_offset =
            KernelBytecode::Next(start + iter.PcOffset()) - start;
        if (pc_offset < lowest_pc_offset) {
          lowest_pc_offset = pc_offset;
        }
      }
    }
#else
    UNREACHABLE();
#endif
  } else {
    ASSERT(func.HasCode());
    ASSERT(!func.HasOptimizedCode());
    code = func.unoptimized_code();
    ASSERT(!code.IsNull());
    start = code.PayloadStart();
    const auto& desc = PcDescriptors::Handle(zone, code.pc_descriptors());
    PcDescriptors::Iterator iter(desc, kSafepointKind);
    while (iter.MoveNext()) {
      if (iter.TokenPos() == loc->token_pos_) {
        if (iter.PcOffset() < lowest_pc_offset) {
          lowest_pc_offset = iter.PcOffset();
          lowest_kind = iter.Kind();
        }
      }
    }
  }
  if (lowest_pc_offset == kUwordMax) {
    return;
  }

  uword lowest_pc = start + lowest_pc_offset;
  CodeBreakpoint* code_bpt = GetCodeBreakpoint(lowest_pc);
  if (code_bpt == nullptr) {
    // No code breakpoint for this code exists; create one.
    if (!bytecode.IsNull()) {
      code_bpt = new CodeBreakpoint(bytecode, loc, lowest_pc);
    } else {
      code_bpt = new CodeBreakpoint(code, loc, lowest_pc, lowest_kind);
    }
    if (FLAG_verbose_debug) {
      OS::PrintErr("Setting code breakpoint at pos %s pc %#" Px " offset %#" Px
                   "\n",
                   loc->token_pos().ToCString(), lowest_pc, lowest_pc - start);
    }
    RegisterCodeBreakpoint(code_bpt);
  } else {
    if (FLAG_verbose_debug) {
      OS::PrintErr(
          "Adding location to existing code breakpoint at pos %s pc %#" Px
          " offset %#" Px "\n",
          loc->token_pos().ToCString(), lowest_pc, lowest_pc - start);
    }
    if (!code_bpt->HasBreakpointLocation(loc)) {
      code_bpt->AddBreakpointLocation(loc);
    }
  }
  if (loc->AnyEnabled()) {
    code_bpt->Enable();
  }
}

void GroupDebugger::MakeCodeBreakpointAt(const Function& func,
                                         BreakpointLocation* loc) {
  auto thread = Thread::Current();
  if (thread->IsInStoppedMutatorsScope()) {
    MakeCodeBreakpointAtUnsafe(thread, func, loc);
  } else {
    SafepointWriteRwLocker sl(thread, code_breakpoints_lock());
    MakeCodeBreakpointAtUnsafe(thread, func, loc);
  }
}

ErrorPtr Debugger::FindAndCompileMatchingFunctions(
    const GrowableHandlePtrArray<const Script>& scripts,
    TokenPosition start_pos,
    TokenPosition end_pos,
    GrowableObjectArray& code_function_list) const {
  auto thread = Thread::Current();
  auto zone = thread->zone();
  Script& script = Script::Handle(zone);
  Object& ensure_has_code_result = Object::Handle(zone);
  for (intptr_t i = 0; i < scripts.length(); ++i) {
    script = scripts.At(i).ptr();
    ClosureFunctionsCache::ForAllClosureFunctions(
        [&](const Function& function) {
          ASSERT(!function.IsNull());
          if ((function.token_pos() == start_pos) &&
              (function.end_token_pos() == end_pos) &&
              (function.script() == script.ptr()) && function.is_debuggable()) {
            // If we've found a matching function, ensure it's compiled so
            // the breakpoint currently being set can be resolved immediately.
            ensure_has_code_result = function.EnsureHasCodeNoThrow();
            if (ensure_has_code_result.IsError()) {
              return false;  // Stop iterating.
            }
            code_function_list.Add(function);
            ASSERT(!function.HasImplicitClosureFunction());
          }
          return true;  // Continue iterating.
        });
    if (ensure_has_code_result.IsError()) {
      return Error::Cast(ensure_has_code_result).ptr();
    }

    Class& cls = Class::Handle(zone);
    Array& functions = Array::Handle(zone);
    Function& function = Function::Handle(zone);
    Array& fields = Array::Handle(zone);
    Field& field = Field::Handle(zone);

    const ClassTable& class_table = *isolate_->group()->class_table();
    const intptr_t num_classes = class_table.NumCids();
    const intptr_t num_tlc_classes = class_table.NumTopLevelCids();
    for (intptr_t i = 1; i < num_classes + num_tlc_classes; i++) {
      const intptr_t cid =
          i < num_classes ? i
                          : ClassTable::CidFromTopLevelIndex(i - num_classes);
      if (class_table.HasValidClassAt(cid)) {
        cls = class_table.At(cid);
        // If the class is not finalized, e.g. if it hasn't been parsed
        // yet entirely, we can ignore it. If it contains a function with
        // an unresolved breakpoint, we will detect it if and when the
        // function gets compiled.
        if (!cls.is_finalized()) {
          continue;
        }
        // Note: we need to check the functions of this class even if
        // the class is defined in a different 'script'. There could
        // be mixin functions from the given script in this class.
        functions = cls.current_functions();
        if (!functions.IsNull()) {
          const intptr_t num_functions = functions.Length();
          for (intptr_t pos = 0; pos < num_functions; pos++) {
            function ^= functions.At(pos);
            ASSERT(!function.IsNull());
            bool function_added = false;
            if (function.is_debuggable() && function.token_pos() == start_pos &&
                function.end_token_pos() == end_pos &&
                function.script() == script.ptr()) {
              // If we've found a matching function, ensure it's compiled so
              // the breakpoint currently being set can be resolved immediately.
              ensure_has_code_result = function.EnsureHasCodeNoThrow();
              if (ensure_has_code_result.IsError()) {
                return Error::Cast(ensure_has_code_result).ptr();
              }
              code_function_list.Add(function);
              function_added = true;
            }
            if (function_added && function.HasImplicitClosureFunction()) {
              function = function.ImplicitClosureFunction();
              if (function.is_debuggable()) {
                // Ensure that the implicit closure function is compiled so the
                // breakpoint currently being set can be resolved immediately.
                ensure_has_code_result = function.EnsureHasCodeNoThrow();
                if (ensure_has_code_result.IsError()) {
                  return Error::Cast(ensure_has_code_result).ptr();
                }
                code_function_list.Add(function);
              }
            }
          }
        }
        fields = cls.fields();
        if (!fields.IsNull()) {
          const intptr_t num_fields = fields.Length();
          for (intptr_t pos = 0; pos < num_fields; pos++) {
            field ^= fields.At(pos);
            ASSERT(!field.IsNull());
            if (field.Script() != script.ptr()) {
              continue;
            }
            if (!field.has_nontrivial_initializer()) {
              continue;
            }
            function = field.EnsureInitializerFunction();
            ASSERT(!function.IsNull());
            if (function.is_debuggable() && function.HasCode() &&
                function.token_pos() == start_pos &&
                function.end_token_pos() == end_pos &&
                function.script() == script.ptr()) {
              ensure_has_code_result = function.EnsureHasCodeNoThrow();
              if (ensure_has_code_result.IsError()) {
                return Error::Cast(ensure_has_code_result).ptr();
              }
              code_function_list.Add(function);
            }
          }
        }
      }
    }
  }
  return Error::null();
}

static void UpdateBestFit(Function* best_fit, const Function& func) {
  if (best_fit->IsNull()) {
    *best_fit = func.ptr();
  } else if ((best_fit->token_pos().IsSynthetic() ||
              func.token_pos().IsSynthetic() ||
              (best_fit->token_pos() < func.token_pos())) &&
             (func.end_token_pos() <= best_fit->end_token_pos())) {
    *best_fit = func.ptr();
  }
}

// If a best fit function is found, stores that function in |best_fit| and
// returns |true|.
// Note that in some cases, there may be a closure that is a better fit that the
// function returned in |best_fit|, because |FindBestFit| is only able to detect
// that closure if the function containing it has already been compiled.
bool Debugger::FindBestFit(const Script& script,
                           TokenPosition token_pos,
                           TokenPosition last_token_pos,
                           Function* best_fit) {
  auto thread = Thread::Current();
  auto isolate_group = thread->isolate_group();
  Zone* zone = thread->zone();
  Class& cls = Class::Handle(zone);

  // A single script can belong to several libraries because of mixins.
  // Go through all libraries and for each that contains the script, try to find
  // a fit there.
  // Return the first fit found, but if a library doesn't contain a fit,
  // process the next one.
  const GrowableObjectArray& libs = GrowableObjectArray::Handle(
      zone, isolate_group->object_store()->libraries());
  Library& lib = Library::Handle(zone);
  for (int i = 0; i < libs.Length(); i++) {
    lib ^= libs.At(i);
    ASSERT(!lib.IsNull());
    const Array& scripts = Array::Handle(zone, lib.LoadedScripts());
    bool lib_has_script = false;
    for (intptr_t j = 0; j < scripts.Length(); j++) {
      if (scripts.At(j) == script.ptr()) {
        lib_has_script = true;
        break;
      }
    }
    if (!lib_has_script) {
      continue;
    }

    if (!lib.IsDebuggable()) {
      if (FLAG_verbose_debug) {
        OS::PrintErr("Library '%s' has been marked as non-debuggable\n",
                     lib.ToCString());
      }
      continue;
    }

    const String& script_url = String::Handle(zone, script.url());
    ClosureFunctionsCache::ForAllClosureFunctions([&](const Function& fun) {
      if (fun.script() == script.ptr() &&
          FunctionOverlaps(fun, script_url, token_pos, last_token_pos)) {
        // Select the inner most closure.
        UpdateBestFit(best_fit, fun);
      }
      return true;  // Continue iteration
    });

    if (!best_fit->IsNull()) {
      // The inner most closure found will be the best fit. Going
      // over class functions below will not help in any further
      // narrowing.
      return true;
    }

    Array& functions = Array::Handle(zone);
    Function& function = Function::Handle(zone);
    Array& fields = Array::Handle(zone);
    Field& field = Field::Handle(zone);
    Error& error = Error::Handle(zone);

    const ClassTable& class_table = *isolate_->group()->class_table();
    const intptr_t num_classes = class_table.NumCids();
    const intptr_t num_tlc_classes = class_table.NumTopLevelCids();
    for (intptr_t i = 1; i < num_classes + num_tlc_classes; i++) {
      const intptr_t cid =
          i < num_classes ? i
                          : ClassTable::CidFromTopLevelIndex(i - num_classes);
      if (!class_table.HasValidClassAt(cid)) {
        continue;
      }
      cls = class_table.At(cid);
      // This class is relevant to us only if it belongs to the
      // library to which |script| belongs.
      if (cls.library() != lib.ptr()) {
        continue;
      }
      // Parse class definition if not done yet.
      error = cls.EnsureIsFinalized(Thread::Current());
      if (!error.IsNull()) {
        // Ignore functions in this class.
        // TODO(hausner): Should we propagate this error? How?
        // EnsureIsFinalized only returns an error object if there
        // is no longjump base on the stack.
        continue;
      }
      functions = cls.current_functions();
      if (!functions.IsNull()) {
        const intptr_t num_functions = functions.Length();
        for (intptr_t pos = 0; pos < num_functions; pos++) {
          function ^= functions.At(pos);
          ASSERT(!function.IsNull());
          if (IsImplicitFunction(function) || function.is_synthetic() ||
              !function.token_pos().IsReal() ||
              !function.end_token_pos().IsReal() || !function.is_debuggable()) {
            // We skip implicit functions and synthetic functions because they
            // do not have user specifiable source locations. We also skip
            // functions marked as undebuggable.
            continue;
          }
          if (FunctionOverlaps(function, script_url, token_pos,
                               last_token_pos)) {
            // Closures and inner functions within a class method are not
            // present in the functions of a class. Hence, we can return
            // right away as looking through other functions of a class
            // will not narrow down to any inner function/closure.
            *best_fit = function.ptr();
            return true;
          }
        }
      }
      // If none of the functions in the class contain token_pos, then we check
      // if it falls within a function literal initializer of a field.
      fields = cls.fields();
      if (!fields.IsNull()) {
        const intptr_t num_fields = fields.Length();
        for (intptr_t pos = 0; pos < num_fields; pos++) {
          TokenPosition start = TokenPosition::kNoSource;
          TokenPosition end = TokenPosition::kNoSource;
          field ^= fields.At(pos);
          ASSERT(!field.IsNull());
          if (field.Script() != script.ptr()) {
            // The field should be defined in the script we want to set
            // the breakpoint in.
            continue;
          }
          if (!field.has_nontrivial_initializer()) {
            continue;
          }
          start = field.token_pos();
          end = field.end_token_pos();
          if (token_pos.IsWithin(start, end) ||
              start.IsWithin(token_pos, last_token_pos)) {
            *best_fit = field.EnsureInitializerFunction();
            return true;
          }
        }
      }
    }
  }
  return false;
}

// If |requested_column| is |-1|, then |exact_token_pos| must be
// |TokenPosition::kNoSource|.
BreakpointLocation* Debugger::SetCodeBreakpoints(
    const GrowableHandlePtrArray<const Script>& scripts,
    TokenPosition token_pos,
    TokenPosition last_token_pos,
    intptr_t requested_line,
    intptr_t requested_column,
    TokenPosition exact_token_pos,
    const GrowableObjectArray& functions) {
  ASSERT(
      (requested_column == -1 && exact_token_pos == TokenPosition::kNoSource) ||
      requested_column > -1);

  Function& function = Function::Handle();
  function ^= functions.At(0);
  TokenPosition breakpoint_pos = ResolveBreakpointPos(
      function, token_pos, last_token_pos, requested_column, exact_token_pos);
  if (!breakpoint_pos.IsReal()) {
    return nullptr;
  }
  const String& script_url = String::Handle(scripts.At(0).url());
  BreakpointLocation* loc =
      GetResolvedBreakpointLocation(script_url, breakpoint_pos);
  if (loc == nullptr) {
    // Find an existing unresolved breakpoint location.
    loc = GetBreakpointLocation(script_url, token_pos, requested_line,
                                requested_column);
  }
  if (loc == nullptr) {
    loc = new BreakpointLocation(this, scripts, breakpoint_pos, breakpoint_pos,
                                 requested_line, requested_column);
    RegisterBreakpointLocation(loc);
  }
  // A source breakpoint for this location may already exists, but it may
  // not yet be resolved in code.
  if (loc->IsResolved()) {
    return loc;
  }
  loc->SetResolved(function, breakpoint_pos);

  // Create code breakpoints for all compiled functions we found.
  Function& func = Function::Handle();
  const intptr_t num_functions = functions.Length();
  for (intptr_t i = 0; i < num_functions; i++) {
    func ^= functions.At(i);
    ASSERT(func.HasCode());
    group_debugger()->MakeCodeBreakpointAt(func, loc);
  }
  if (FLAG_verbose_debug) {
    intptr_t line_number = -1;
    intptr_t column_number = -1;
    scripts.At(0).GetTokenLocation(breakpoint_pos, &line_number,
                                   &column_number);
    OS::PrintErr("Resolved code breakpoint for function '%s' at line %" Pd
                 " col %" Pd "\n",
                 func.ToFullyQualifiedCString(), line_number, column_number);
  }
  return loc;
}

#if !defined(DART_PRECOMPILED_RUNTIME)
static TokenPosition FindExactTokenPosition(const Script& script,
                                            TokenPosition start_of_line,
                                            intptr_t column_number);
#endif  // !defined(DART_PRECOMPILED_RUNTIME)

ErrorPtr Debugger::SetBreakpoint(
    const Script& script,
    TokenPosition token_pos,
    TokenPosition last_token_pos,
    intptr_t requested_line,
    intptr_t requested_column,
    const Function& function,
    BreakpointLocation** result_breakpoint_location) {
  GrowableHandlePtrArray<const Script> scripts(Thread::Current()->zone(), 1);
  scripts.Add(script);
  return SetBreakpoint(scripts, token_pos, last_token_pos, requested_line,
                       requested_column, function, result_breakpoint_location);
}

ErrorPtr Debugger::SetBreakpoint(
    const GrowableHandlePtrArray<const Script>& scripts,
    TokenPosition token_pos,
    TokenPosition last_token_pos,
    intptr_t requested_line,
    intptr_t requested_column,
    const Function& function,
    BreakpointLocation** result_breakpoint_location) {
  ASSERT(scripts.length() > 0);
  ASSERT(result_breakpoint_location != nullptr);
  Function& func = Function::Handle();
  const Script& script = scripts.At(0);
  if (function.IsNull()) {
    if (!FindBestFit(script, token_pos, last_token_pos, &func)) {
      return Object::no_debuggable_code_error().ptr();
    }
    ASSERT(!func.IsNull());
    // There may be closures that were not considered by the first call to
    // |FindBestFit| because the functions containing them were not yet
    // compiled. So, we must recursively compile functions until we converge
    // at a true best fit function.
    Function& tmp = Function::Handle();
    Object& ensure_has_code_result = Object::Handle();
    do {
      ensure_has_code_result = func.EnsureHasCodeNoThrow();
      if (ensure_has_code_result.IsError()) {
        return Error::Cast(ensure_has_code_result).ptr();
      }
      tmp = func.ptr();
      FindBestFit(script, token_pos, last_token_pos, &func);
      ASSERT(!func.IsNull());
    } while (tmp.ptr() != func.ptr());
  } else {
    func = function.ptr();
    if (!func.token_pos().IsReal()) {
      // Missing source positions?
      return Object::no_debuggable_code_error().ptr();
    }
  }

  if (!func.IsNull()) {
    // There may be more than one function object for a given function in source
    // code. There may be implicit closure functions, and there may be copies of
    // mixin functions. Compile and collect all functions whose source code
    // range matches exactly the best fit function we found. We compile all of
    // theses functions eagerly so that the breakpoint currently being set can
    // be resolved immediately.
    GrowableObjectArray& code_functions =
        GrowableObjectArray::Handle(GrowableObjectArray::New());
    const Error& error = Error::Handle(FindAndCompileMatchingFunctions(
        scripts, func.token_pos(), func.end_token_pos(), code_functions));
    if (!error.IsNull()) {
      return error.ptr();
    }

    if (code_functions.Length() > 0) {
      // One or more function object containing this breakpoint location
      // have already been compiled. We can resolve the breakpoint now.
      // If requested_column is larger than zero, [token_pos, last_token_pos]
      // governs one single line of code.
      TokenPosition exact_token_pos = TokenPosition::kNoSource;
#if !defined(DART_PRECOMPILED_RUNTIME)
      if (token_pos != last_token_pos && requested_column >= 0) {
        exact_token_pos =
            FindExactTokenPosition(script, token_pos, requested_column);
      }
#endif  // !defined(DART_PRECOMPILED_RUNTIME)
      BreakpointLocation* loc = nullptr;
      // Ensure that code stays deoptimized (and background compiler disabled)
      // until we have installed the breakpoint (at which point the compiler
      // will not try to optimize it anymore).
      RunWithStoppedDeoptimizedWorld([&] {
        loc = SetCodeBreakpoints(scripts, token_pos, last_token_pos,
                                 requested_line, requested_column,
                                 exact_token_pos, code_functions);
      });
      if (loc != nullptr) {
        *result_breakpoint_location = loc;
        return Error::null();
      }
    }
  }
  // There is no debuggable code at all at |token_pos|, so we leave
  // |*result_breakpoint_location| as null.
  return Error::null();
}

// Synchronize the enabled/disabled state of all code breakpoints
// associated with the breakpoint location loc.
void GroupDebugger::SyncBreakpointLocation(BreakpointLocation* loc) {
  bool any_enabled = loc->AnyEnabled();
  SafepointWriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
  CodeBreakpoint* cbpt = code_breakpoints_;
  while (cbpt != nullptr) {
    if (cbpt->HasBreakpointLocation(loc)) {
      if (any_enabled) {
        cbpt->Enable();
      } else {
        cbpt->Disable();
      }
    }
    cbpt = cbpt->next();
  }
}

ErrorPtr Debugger::SetBreakpointAtEntry(const Function& target_function,
                                        bool single_shot,
                                        Breakpoint** result_breakpoint) {
  ASSERT(!target_function.IsNull());
  ASSERT(result_breakpoint != nullptr);
  if (!target_function.is_debuggable()) {
    return Object::no_debuggable_code_error().ptr();
  }
  const Script& script = Script::Handle(target_function.script());
  BreakpointLocation* bpt_location = nullptr;
  const Error& error = Error::Handle(SetBreakpoint(
      script, target_function.token_pos(), target_function.end_token_pos(), -1,
      -1 /* no requested line/col */, target_function, &bpt_location));
  if (!error.IsNull()) {
    return error.ptr();
  }

  ASSERT(bpt_location != nullptr);
  if (single_shot) {
    *result_breakpoint = bpt_location->AddSingleShot(this);
  } else {
    *result_breakpoint = bpt_location->AddRepeated(this);
  }
  return Error::null();
}

ErrorPtr Debugger::SetBreakpointAtActivation(const Instance& closure,
                                             bool single_shot,
                                             Breakpoint** result_breakpoint) {
  ASSERT(result_breakpoint != nullptr);

  if (!closure.IsClosure()) {
    return Object::no_debuggable_code_error().ptr();
  }
  const Function& func = Function::Handle(Closure::Cast(closure).function());
  const Script& script = Script::Handle(func.script());
  BreakpointLocation* bpt_location = nullptr;
  const Error& error = Error::Handle(
      SetBreakpoint(script, func.token_pos(), func.end_token_pos(), -1,
                    -1 /* no line/col */, func, &bpt_location));
  if (!error.IsNull()) {
    return error.ptr();
  }

  ASSERT(bpt_location != nullptr);
  *result_breakpoint =
      bpt_location->AddBreakpoint(this, Closure::Cast(closure), single_shot);
  return Error::null();
}

Breakpoint* Debugger::BreakpointAtActivation(const Instance& closure) {
  if (!closure.IsClosure()) {
    return nullptr;
  }

  BreakpointLocation* loc = breakpoint_locations_;
  while (loc != nullptr) {
    Breakpoint* bpt = loc->breakpoints();
    while (bpt != nullptr) {
      if (closure.ptr() == bpt->closure()) {
        return bpt;
      }
      bpt = bpt->next();
    }
    loc = loc->next();
  }

  return nullptr;
}

void Debugger::SetBreakpointAtResumption(const Object& function_data) {
  ASSERT(!function_data.IsNull());
  ASSERT(function_data.IsInstance());
  breakpoints_at_resumption_.Add(function_data.ptr());
  isolate_->set_has_resumption_breakpoints(true);
}

void Debugger::ResumptionBreakpoint() {
  ASSERT(!breakpoints_at_resumption_.is_empty());
  ASSERT(isolate_->has_resumption_breakpoints());

  ActivationFrame* top_frame = TopDartFrame();
  ASSERT(top_frame->function().IsSuspendableFunction());
  const auto& function_data =
      Object::Handle(top_frame->GetSuspendableFunctionData());

  for (intptr_t i = 0, n = breakpoints_at_resumption_.length(); i < n; ++i) {
    if (breakpoints_at_resumption_[i] == function_data.ptr()) {
      breakpoints_at_resumption_.RemoveAt(i);
      if (breakpoints_at_resumption_.is_empty()) {
        isolate_->set_has_resumption_breakpoints(false);
      }
      if (FLAG_verbose_debug) {
        OS::PrintErr(
            "ResumptionBreakpoint - hit a breakpoint, continue single "
            "stepping\n");
      }
#if defined(DART_DYNAMIC_MODULES)
      if (top_frame->IsInterpreted()) {
        // The interpreter calls the single step handler on every instruction,
        // so set the last stepping fp/position to the current fp/position when
        // stepping over so the debugger doesn't pause until it reaches
        // a _new_ source position.
        last_stepping_fp_ = top_frame->fp();
        last_stepping_pos_ = top_frame->TokenPos();
      }
#endif
      EnterSingleStepMode();
      return;
    }
  }
}

ErrorPtr Debugger::SetBreakpointAtLineCol(const String& script_url,
                                          intptr_t line_number,
                                          intptr_t column_number,
                                          Breakpoint** result_breakpoint) {
  // Prevent future tests from calling this function in the wrong
  // execution state.  If you hit this assert, consider using
  // Dart_SetBreakpoint instead.
  ASSERT(Thread::Current()->execution_state() == Thread::kThreadInVM);
  ASSERT(result_breakpoint != nullptr);

  BreakpointLocation* loc = nullptr;
  const Error& error = Error::Handle(BreakpointLocationAtLineCol(
      script_url, line_number, column_number, &loc));
  if (!error.IsNull()) {
    return error.ptr();
  }

  ASSERT(loc != nullptr);
  *result_breakpoint = loc->AddRepeated(this);
  return Error::null();
}

ErrorPtr Debugger::BreakpointLocationAtLineCol(
    const String& script_url,
    intptr_t line_number,
    intptr_t column_number,
    BreakpointLocation** result_breakpoint_location) {
  ASSERT(result_breakpoint_location != nullptr);

  Zone* zone = Thread::Current()->zone();
  Library& lib = Library::Handle(zone);
  GrowableHandlePtrArray<const Script> scripts(zone, 1);
  const GrowableObjectArray& libs = GrowableObjectArray::Handle(
      isolate_->group()->object_store()->libraries());
  bool is_package = script_url.StartsWith(Symbols::PackageScheme());
  bool is_dart_colon = script_url.StartsWith(Symbols::DartScheme());
  Script& script_for_lib = Script::Handle(zone);
  for (intptr_t i = 0; i < libs.Length(); i++) {
    lib ^= libs.At(i);
    // Ensure that all top-level members are loaded so their scripts
    // are available for look up. When certain script only contains
    // top level functions, scripts could still be loaded correctly.
    lib.EnsureTopLevelClassIsFinalized();
    bool useResolvedUri = !is_package && !is_dart_colon;
    script_for_lib = lib.LookupScript(script_url, useResolvedUri);
    if (!script_for_lib.IsNull()) {
      scripts.Add(script_for_lib);
    }
  }
  if (scripts.length() == 0) {
    // No script found with given url. Create a latent breakpoint which
    // will be set if the url is loaded later.
    BreakpointLocation* latent_bpt =
        GetLatentBreakpoint(script_url, line_number, column_number);
    if (FLAG_verbose_debug) {
      OS::PrintErr(
          "Set latent breakpoint in url '%s' at "
          "line %" Pd " col %" Pd "\n",
          script_url.ToCString(), line_number, column_number);
    }
    *result_breakpoint_location = latent_bpt;
    return Error::null();
  }
  TokenPosition first_token_idx = TokenPosition::kNoSource;
  TokenPosition last_token_idx = TokenPosition::kNoSource;
  // Assume all scripts with the same URL have the same token positions.
  scripts.At(0).TokenRangeAtLine(line_number, &first_token_idx,
                                 &last_token_idx);
  if (!first_token_idx.IsReal()) {
    // Script does not contain the given line number.
    if (FLAG_verbose_debug) {
      OS::PrintErr("Script '%s' does not contain line number %" Pd "\n",
                   script_url.ToCString(), line_number);
    }
    return Object::no_debuggable_code_error().ptr();
  } else if (!last_token_idx.IsReal()) {
    // Line does not contain any tokens.
    if (FLAG_verbose_debug) {
      OS::PrintErr("No executable code at line %" Pd " in '%s'\n", line_number,
                   script_url.ToCString());
    }
    return Object::no_debuggable_code_error().ptr();
  }

  ASSERT(first_token_idx <= last_token_idx);
  Error& error = Error::Handle();
  while ((*result_breakpoint_location == nullptr) &&
         (first_token_idx <= last_token_idx)) {
    error = SetBreakpoint(scripts, first_token_idx, last_token_idx, line_number,
                          column_number, Function::Handle(),
                          result_breakpoint_location);
    if (!error.IsNull() &&
        error.ptr() != Object::no_debuggable_code_error().ptr()) {
      // We do not return an error immediately if |SetBreakpoint| returns
      // |Object::no_debuggable_code_error().ptr()|, because there is a chance
      // that |SetBreakpoint| will succeed if called again after
      // |first_token_idx| is advanced.
      return error.ptr();
    }
    first_token_idx = first_token_idx.Next();
  }
  if (*result_breakpoint_location == nullptr) {
    if (FLAG_verbose_debug) {
      OS::PrintErr("No executable code at line %" Pd " in '%s'\n", line_number,
                   script_url.ToCString());
    }
    return Object::no_debuggable_code_error().ptr();
  }
  return Error::null();
}

// Return innermost closure contained in 'function' that contains
// the given token position.
static FunctionPtr FindInnermostClosure(Zone* zone,
                                        const Function& function,
                                        TokenPosition token_pos) {
  ASSERT(function.end_token_pos().IsReal());
  const TokenPosition& func_start = function.token_pos();
  const Script& outer_origin = Script::Handle(zone, function.script());

  Function& best_fit = Function::Handle(zone);
  ClosureFunctionsCache::ForAllClosureFunctions([&](const Function& closure) {
    const TokenPosition& closure_start = closure.token_pos();
    const TokenPosition& closure_end = closure.end_token_pos();
    // We're only interested in closures that have real ending token positions.
    // The starting token position can be synthetic.
    if (closure_end.IsReal() && (function.end_token_pos() > closure_end) &&
        (!closure_start.IsReal() || !func_start.IsReal() ||
         (closure_start > func_start)) &&
        token_pos.IsWithin(closure_start, closure_end) &&
        (closure.script() == outer_origin.ptr())) {
      UpdateBestFit(&best_fit, closure);
    }
    return true;  // Continue iteration.
  });
  return best_fit.ptr();
}

bool GroupDebugger::EnsureLocationIsInFunction(Zone* zone,
                                               const Function& function,
                                               BreakpointLocation* location) {
  const String& url = String::Handle(zone, location->url());
  if (!FunctionOverlaps(function, url, location->token_pos(),
                        location->end_token_pos())) {
    return false;
  }

  TokenPosition token_pos = location->token_pos();
#if !defined(DART_PRECOMPILED_RUNTIME)
  TokenPosition end_token_pos = location->end_token_pos();
  if (token_pos != end_token_pos && location->requested_column_number() >= 0) {
    // Narrow down the token position range to a single value
    // if requested column number is provided so that inner
    // Closure won't be missed.
    const Script& script = Script::Handle(location->script());
    token_pos = FindExactTokenPosition(script, token_pos,
                                       location->requested_column_number());
  }
#endif  // !defined(DART_PRECOMPILED_RUNTIME)
  const Function& inner_function =
      Function::Handle(zone, FindInnermostClosure(zone, function, token_pos));
  if (!inner_function.IsNull()) {
    if (FLAG_verbose_debug) {
      OS::PrintErr(
          "Pending breakpoint remains unresolved in "
          "inner function '%s'\n",
          inner_function.ToFullyQualifiedCString());
    }
    return false;
  }

  // There is no local function within function that contains the
  // breakpoint token position.
  return true;
}

void GroupDebugger::NotifyCompilation(const Function& function) {
  if (!function.is_debuggable()) {
    return;
  }
  Function& resolved_function = Function::Handle(function.ptr());
  auto thread = Thread::Current();
  auto zone = thread->zone();

  // Going through BreakpointLocations of all isolates and debuggers looking
  // for those that can be resolved and added code breakpoints at now.
  //
  // The check below is used instead of breakpoint_locations_lock acquisition.
  // We don't need to acquire the lock if always run with stopped mutators.
  // We can't acquire the lock if we run with stopped mutators as that could
  // result in deadlock.
  RELEASE_ASSERT(thread->IsInStoppedMutatorsScope());
  for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
    BreakpointLocation* location = breakpoint_locations_.At(i);
    if (EnsureLocationIsInFunction(zone, resolved_function, location)) {
      // All mutators are stopped (see RELEASE_ASSERT above). We temporarily
      // enter the isolate for which the breakpoint was registered.
      // The code path below may issue service events which will use the active
      // isolate's object-id ring for naming VM objects.
      ActiveIsolateScope active_isolate(thread,
                                        location->debugger()->isolate());

      // Ensure the location is resolved for the original function.
      TokenPosition exact_token_pos = TokenPosition::kNoSource;
#if !defined(DART_PRECOMPILED_RUNTIME)
      if (location->token_pos() != location->end_token_pos() &&
          location->requested_column_number() >= 0) {
        exact_token_pos = FindExactTokenPosition(
            Script::Handle(location->script()), location->token_pos(),
            location->requested_column_number());
      }
#endif  // !defined(DART_PRECOMPILED_RUNTIME)
      location->EnsureIsResolved(function, exact_token_pos);
      if (FLAG_verbose_debug) {
        Breakpoint* bpt = location->breakpoints();
        while (bpt != nullptr) {
          OS::PrintErr("Setting breakpoint %" Pd " for %s '%s'\n", bpt->id(),
                       function.IsClosureFunction() ? "closure" : "function",
                       function.ToFullyQualifiedCString());
          bpt = bpt->next();
        }
      }
      MakeCodeBreakpointAt(function, location);
    }
  }
}

void GroupDebugger::VisitObjectPointers(ObjectPointerVisitor* visitor) {
  CodeBreakpoint* cbpt = code_breakpoints_;
  while (cbpt != nullptr) {
    cbpt->VisitObjectPointers(visitor);
    cbpt = cbpt->next();
  }
}

// static
void Debugger::VisitObjectPointers(ObjectPointerVisitor* visitor) {
  ASSERT(visitor != nullptr);
  BreakpointLocation* loc = breakpoint_locations_;
  while (loc != nullptr) {
    loc->VisitObjectPointers(visitor);
    loc = loc->next();
  }
  loc = latent_locations_;
  while (loc != nullptr) {
    loc->VisitObjectPointers(visitor);
    loc = loc->next();
  }
  for (intptr_t i = 0, n = breakpoints_at_resumption_.length(); i < n; ++i) {
    visitor->VisitPointer(&breakpoints_at_resumption_[i]);
  }
}

void Debugger::Pause(ServiceEvent* event) {
  ASSERT(event->IsPause());      // Should call InvokeEventHandler instead.
  ASSERT(!ignore_breakpoints_);  // We shouldn't get here when ignoring bpts.
  ASSERT(!IsPaused());           // No recursive pausing.

  pause_event_ = event;
  pause_event_->UpdateTimestamp();

  // We are about to invoke the debugger's event handler. Disable
  // interrupts for this thread while waiting for debug commands over
  // the service protocol.
  {
    Thread* thread = Thread::Current();
    DisableThreadInterruptsScope dtis(thread);
    TIMELINE_DURATION(thread, Debugger, "Debugger Pause");

    // Send the pause event.
    Service::HandleEvent(event);

    {
      TransitionVMToNative transition(thread);
      isolate_->PauseEventHandler();
    }

    // Notify the service that we have resumed.
    const Error& error = Error::Handle(Thread::Current()->sticky_error());
    ASSERT(error.IsNull() || error.IsUnwindError() ||
           error.IsUnhandledException());

    // Only send a resume event when the isolate is not unwinding.
    if (!error.IsUnwindError()) {
      ServiceEvent resume_event(event->isolate(), ServiceEvent::kResume);
      resume_event.set_top_frame(event->top_frame());
      Service::HandleEvent(&resume_event);
    }
  }

  group_debugger()->Pause();
  pause_event_ = nullptr;
}

void GroupDebugger::Pause() {
  SafepointWriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
  if (needs_breakpoint_cleanup_) {
    RemoveUnlinkedCodeBreakpoints();
  }
}

void Debugger::EnterSingleStepMode() {
  ResetSteppingFramePointer();
  NotifySingleStepping(true);
}

void Debugger::ResetSteppingFramePointer() {
  stepping_fp_ = 0;
#if defined(DART_DYNAMIC_MODULES)
  stepping_fp_from_interpreted_frame_ = false;
#endif
}

void Debugger::SetSyncSteppingFramePointer(DebuggerStackTrace* stack_trace) {
  if (stack_trace->Length() > 0) {
    auto* const frame = stack_trace->FrameAt(0);
    stepping_fp_ = frame->fp();
#if defined(DART_DYNAMIC_MODULES)
    stepping_fp_from_interpreted_frame_ = frame->IsInterpreted();
    // The interpreter calls the single step handler on every instruction,
    // so set the last stepping fp/position to the current fp/position when
    // stepping over so the debugger doesn't pause until it reaches
    // a _new_ source position.
    last_stepping_fp_ = frame->fp();
    last_stepping_pos_ = frame->TokenPos();
#endif
  } else {
    stepping_fp_ = 0;
#if defined(DART_DYNAMIC_MODULES)
    stepping_fp_from_interpreted_frame_ = false;
#endif
  }
}

void Debugger::HandleSteppingRequest(bool skip_next_step /* = false */) {
  ResetSteppingFramePointer();
  if (resume_action_ == kStepInto) {
    // When single stepping, we need to deoptimize because we might be
    // stepping into optimized code.  This happens in particular if
    // the isolate has been interrupted, but can happen in other cases
    // as well.  We need to deoptimize the world in case we are about
    // to call an optimized function.
    NotifySingleStepping(true);
    skip_next_step_ = skip_next_step;
    if (FLAG_verbose_debug) {
      OS::PrintErr("HandleSteppingRequest - kStepInto\n");
    }
  } else if (resume_action_ == kStepOver) {
    NotifySingleStepping(true);
    skip_next_step_ = skip_next_step;
    SetSyncSteppingFramePointer(stack_trace_);
    if (FLAG_verbose_debug) {
      OS::PrintErr("HandleSteppingRequest - kStepOver stepping_fp=%" Px "\n",
                   stepping_fp_);
    }
  } else if (resume_action_ == kStepOut) {
    // Check if we have an asynchronous awaiter for the current frame.
    if (async_awaiter_stack_trace_ != nullptr &&
        async_awaiter_stack_trace_->Length() > 2 &&
        async_awaiter_stack_trace_->FrameAt(1)->kind() ==
            ActivationFrame::kAsyncSuspensionMarker) {
      auto awaiter_frame = async_awaiter_stack_trace_->FrameAt(2);
      AsyncStepInto(awaiter_frame->closure());
      if (FLAG_verbose_debug) {
        OS::PrintErr("HandleSteppingRequest - continue to async awaiter %s\n",
                     Function::Handle(awaiter_frame->closure().function())
                         .ToFullyQualifiedCString());
      }
      return;
    }

    // Fall through to synchronous stepping.
    NotifySingleStepping(true);
    // Find topmost caller that is debuggable.
    for (intptr_t i = 1; i < stack_trace_->Length(); i++) {
      ActivationFrame* frame = stack_trace_->FrameAt(i);
      if (frame->IsDebuggable()) {
        stepping_fp_ = frame->fp();
#if defined(DART_DYNAMIC_MODULES)
        stepping_fp_from_interpreted_frame_ = frame->IsInterpreted();
#endif
        break;
      }
    }
    if (FLAG_verbose_debug) {
      OS::PrintErr("HandleSteppingRequest- kStepOut %" Px "\n", stepping_fp_);
    }
  } else if (resume_action_ == kStepRewind) {
    if (FLAG_trace_rewind) {
      OS::PrintErr("Rewinding to frame %" Pd "\n", resume_frame_index_);
      OS::PrintErr(
          "-------------------------\n"
          "All frames...\n\n");
      StackFrameIterator iterator(ValidationPolicy::kDontValidateFrames,
                                  Thread::Current(),
                                  StackFrameIterator::kNoCrossThreadIteration);
      StackFrame* frame = iterator.NextFrame();
      intptr_t num = 0;
      while ((frame != nullptr)) {
        OS::PrintErr("#%04" Pd " %s\n", num++, frame->ToCString());
        frame = iterator.NextFrame();
      }
    }
    RewindToFrame(resume_frame_index_);
    UNREACHABLE();
  }
}

void Debugger::CacheStackTraces(DebuggerStackTrace* stack_trace,
                                DebuggerStackTrace* async_awaiter_stack_trace) {
  ASSERT(stack_trace_ == nullptr);
  stack_trace_ = stack_trace;
  ASSERT(async_awaiter_stack_trace_ == nullptr);
  async_awaiter_stack_trace_ = async_awaiter_stack_trace;
}

void Debugger::ClearCachedStackTraces() {
  stack_trace_ = nullptr;
  async_awaiter_stack_trace_ = nullptr;
}

static intptr_t FindNextRewindFrameIndex(DebuggerStackTrace* stack,
                                         intptr_t frame_index) {
  for (intptr_t i = frame_index + 1; i < stack->Length(); i++) {
    ActivationFrame* frame = stack->FrameAt(i);
    if (frame->IsRewindable()) {
      return i;
    }
  }
  return -1;
}

// Can we rewind to the indicated frame?
static bool CanRewindFrame(intptr_t frame_index, const char** error) {
  // check rewind pc is found
  DebuggerStackTrace* stack = Isolate::Current()->debugger()->StackTrace();
  intptr_t num_frames = stack->Length();
  if (frame_index < 1 || frame_index >= num_frames) {
    if (error != nullptr) {
      *error = Thread::Current()->zone()->PrintToString(
          "Frame must be in bounds [1..%" Pd
          "]: "
          "saw %" Pd "",
          num_frames - 1, frame_index);
    }
    return false;
  }
  ActivationFrame* frame = stack->FrameAt(frame_index);
  if (!frame->IsRewindable()) {
    intptr_t next_index = FindNextRewindFrameIndex(stack, frame_index);
    if (next_index > 0) {
      *error = Thread::Current()->zone()->PrintToString(
          "Cannot rewind to frame %" Pd
          " due to conflicting compiler "
          "optimizations. "
          "Run the vm with --no-prune-dead-locals to disallow these "
          "optimizations. "
          "Next valid rewind frame is %" Pd ".",
          frame_index, next_index);
    } else {
      *error = Thread::Current()->zone()->PrintToString(
          "Cannot rewind to frame %" Pd
          " due to conflicting compiler "
          "optimizations. "
          "Run the vm with --no-prune-dead-locals to disallow these "
          "optimizations.",
          frame_index);
    }
    return false;
  }
  return true;
}

// Given a return address, find the "rewind" pc, which is the pc
// before the corresponding call.
static uword LookupRewindPc(const Code& code, uword return_address) {
  ASSERT(!code.is_optimized());
  ASSERT(code.ContainsInstructionAt(return_address));

  uword pc_offset = return_address - code.PayloadStart();
  const PcDescriptors& descriptors =
      PcDescriptors::Handle(code.pc_descriptors());
  PcDescriptors::Iterator iter(descriptors,
                               UntaggedPcDescriptors::kRewind |
                                   UntaggedPcDescriptors::kIcCall |
                                   UntaggedPcDescriptors::kUnoptStaticCall);
  intptr_t rewind_deopt_id = -1;
  uword rewind_pc = 0;
  while (iter.MoveNext()) {
    if (iter.Kind() == UntaggedPcDescriptors::kRewind) {
      // Remember the last rewind so we don't need to iterator twice.
      rewind_pc = code.PayloadStart() + iter.PcOffset();
      rewind_deopt_id = iter.DeoptId();
    }
    if ((pc_offset == iter.PcOffset()) && (iter.DeoptId() == rewind_deopt_id)) {
      return rewind_pc;
    }
  }
  return 0;
}

void Debugger::RewindToFrame(intptr_t frame_index) {
  Thread* thread = Thread::Current();
  Zone* zone = thread->zone();
  Code& code = Code::Handle(zone);
  Function& function = Function::Handle(zone);

  // Find the requested frame.
  StackFrameIterator iterator(ValidationPolicy::kDontValidateFrames,
                              Thread::Current(),
                              StackFrameIterator::kNoCrossThreadIteration);
  intptr_t current_frame = 0;
  for (StackFrame* frame = iterator.NextFrame(); frame != nullptr;
       frame = iterator.NextFrame()) {
    ASSERT(frame->IsValid());
    if (frame->IsDartFrame()) {
      code = frame->LookupDartCode();
      function = code.function();
      if (!IsFunctionVisible(function)) {
        continue;
      }
      if (code.is_optimized()) {
        intptr_t sub_index = 0;
        for (InlinedFunctionsIterator it(code, frame->pc()); !it.Done();
             it.Advance()) {
          if (current_frame == frame_index) {
            RewindToOptimizedFrame(frame, code, sub_index);
            UNREACHABLE();
          }
          current_frame++;
          sub_index++;
        }
      } else {
        if (current_frame == frame_index) {
          // We are rewinding to an unoptimized frame.
          RewindToUnoptimizedFrame(frame, code);
          UNREACHABLE();
        }
        current_frame++;
      }
    }
  }
  UNIMPLEMENTED();
}

void Debugger::RewindToUnoptimizedFrame(StackFrame* frame, const Code& code) {
  // We will be jumping out of the debugger rather than exiting this
  // function, so prepare the debugger state.
  ClearCachedStackTraces();
  set_resume_action(kContinue);
  resume_frame_index_ = -1;
  EnterSingleStepMode();

  uword rewind_pc = LookupRewindPc(code, frame->pc());
  if (FLAG_trace_rewind && rewind_pc == 0) {
    OS::PrintErr("Unable to find rewind pc for pc(%" Px ")\n", frame->pc());
  }
  ASSERT(rewind_pc != 0);
  if (FLAG_trace_rewind) {
    OS::PrintErr(
        "===============================\n"
        "Rewinding to unoptimized frame:\n"
        "    rewind_pc(0x%" Px " offset:0x%" Px ") sp(0x%" Px ") fp(0x%" Px
        ")\n"
        "===============================\n",
        rewind_pc, rewind_pc - code.PayloadStart(), frame->sp(), frame->fp());
  }
  Exceptions::JumpToFrame(Thread::Current(), rewind_pc, frame->sp(),
                          frame->fp(), true /* clear lazy deopt at target */);
  UNREACHABLE();
}

void Debugger::RewindToOptimizedFrame(StackFrame* frame,
                                      const Code& optimized_code,
                                      intptr_t sub_index) {
  post_deopt_frame_index_ = sub_index;

  // We will be jumping out of the debugger rather than exiting this
  // function, so prepare the debugger state.
  ClearCachedStackTraces();
  set_resume_action(kContinue);
  resume_frame_index_ = -1;
  EnterSingleStepMode();

  if (FLAG_trace_rewind) {
    OS::PrintErr(
        "===============================\n"
        "Deoptimizing frame for rewind:\n"
        "    deopt_pc(0x%" Px ") sp(0x%" Px ") fp(0x%" Px
        ")\n"
        "===============================\n",
        frame->pc(), frame->sp(), frame->fp());
  }
  Thread* thread = Thread::Current();
  thread->set_resume_pc(frame->pc());
  uword deopt_stub_pc = StubCode::DeoptForRewind().EntryPoint();
  Exceptions::JumpToFrame(thread, deopt_stub_pc, frame->sp(), frame->fp(),
                          true /* clear lazy deopt at target */);
  UNREACHABLE();
}

void Debugger::RewindPostDeopt() {
  intptr_t rewind_frame = post_deopt_frame_index_;
  post_deopt_frame_index_ = -1;
  if (FLAG_trace_rewind) {
    OS::PrintErr("Post deopt, jumping to frame %" Pd "\n", rewind_frame);
    OS::PrintErr(
        "-------------------------\n"
        "All frames...\n\n");
    StackFrameIterator iterator(ValidationPolicy::kDontValidateFrames,
                                Thread::Current(),
                                StackFrameIterator::kNoCrossThreadIteration);
    StackFrame* frame = iterator.NextFrame();
    intptr_t num = 0;
    while ((frame != nullptr)) {
      OS::PrintErr("#%04" Pd " %s\n", num++, frame->ToCString());
      frame = iterator.NextFrame();
    }
  }

  Thread* thread = Thread::Current();
  Zone* zone = thread->zone();
  Code& code = Code::Handle(zone);

  StackFrameIterator iterator(ValidationPolicy::kDontValidateFrames,
                              Thread::Current(),
                              StackFrameIterator::kNoCrossThreadIteration);
  intptr_t current_frame = 0;
  for (StackFrame* frame = iterator.NextFrame(); frame != nullptr;
       frame = iterator.NextFrame()) {
    ASSERT(frame->IsValid());
    if (frame->IsDartFrame()) {
      code = frame->LookupDartCode();
      ASSERT(!code.is_optimized());
      if (current_frame == rewind_frame) {
        RewindToUnoptimizedFrame(frame, code);
        UNREACHABLE();
      }
      current_frame++;
    }
  }
}

// static
bool Debugger::IsDebuggable(const Function& func) {
  if (!func.is_debuggable()) {
    return false;
  }
  const Class& cls = Class::Handle(func.Owner());
  const Library& lib = Library::Handle(cls.library());
  return lib.IsDebuggable();
}

void GroupDebugger::RegisterSingleSteppingDebugger(Thread* thread,
                                                   const Debugger* debugger) {
  WriteRwLocker sl(Thread::Current(), single_stepping_set_lock());
  single_stepping_set_.Insert(debugger);
}

void GroupDebugger::UnregisterSingleSteppingDebugger(Thread* thread,
                                                     const Debugger* debugger) {
  WriteRwLocker sl(Thread::Current(), single_stepping_set_lock());
  single_stepping_set_.Remove(debugger);
}

bool GroupDebugger::HasBreakpointUnsafe(Thread* thread,
                                        const Function& function) {
  DEBUG_ASSERT(thread->IsInStoppedMutatorsScope() ||
               breakpoint_locations_lock()->IsCurrentThreadReader());
  // Check if function has any breakpoints.
  String& url = String::Handle(thread->zone());
  for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
    BreakpointLocation* location = breakpoint_locations_.At(i);
    url = location->url();
    if (FunctionOverlaps(function, url, location->token_pos(),
                         location->end_token_pos())) {
      return true;
    }
  }
  return false;
}

bool GroupDebugger::HasBreakpoint(Thread* thread, const Function& function) {
  bool hasBreakpoint = false;
  // Don't need to worry about the lock if mutators are stopped.
  if (thread->IsInStoppedMutatorsScope()) {
    hasBreakpoint = HasBreakpointUnsafe(thread, function);
  } else {
    SafepointReadRwLocker sl(thread, breakpoint_locations_lock());
    hasBreakpoint = HasBreakpointUnsafe(thread, function);
  }
  if (hasBreakpoint) {
    return true;
  }

  // TODO(aam): do we have to iterate over both code breakpoints and
  // breakpoint locations? Wouldn't be sufficient to iterate over only
  // one list? Could you have a CodeBreakpoint without corresponding
  // BreakpointLocation?
  if (HasCodeBreakpointInFunction(function)) {
    return true;
  }

  return false;
}

bool GroupDebugger::IsDebugging(Thread* thread, const Function& function) {
  {
    ReadRwLocker ml(thread, single_stepping_set_lock());
    if (!single_stepping_set_.IsEmpty()) {
      return true;
    }
  }
  return HasBreakpoint(thread, function);
}

void Debugger::set_resume_action(ResumeAction resume_action) {
  auto thread = Thread::Current();
  if (resume_action == kContinue) {
    group_debugger()->UnregisterSingleSteppingDebugger(thread, this);
  } else {
    group_debugger()->RegisterSingleSteppingDebugger(thread, this);
  }
  resume_action_ = resume_action;
}

void Debugger::SignalPausedEvent(ActivationFrame* top_frame, Breakpoint* bpt) {
  set_resume_action(kContinue);
  ResetSteppingFramePointer();
  NotifySingleStepping(false);
  ASSERT(!IsPaused());
  if ((bpt != nullptr) && bpt->is_single_shot()) {
    RemoveBreakpoint(bpt->id());
    bpt = nullptr;
  }

  ServiceEvent event(isolate_, ServiceEvent::kPauseBreakpoint);
  event.set_top_frame(top_frame);
  event.set_breakpoint(bpt);
  event.set_at_async_jump(IsAtAsyncJump(top_frame));
  Pause(&event);
}

static bool IsAtAsyncJump(ActivationFrame* top_frame) {
  Thread* const thread = Thread::Current();
  Zone* const zone = thread->zone();
  if (!top_frame->function().IsAsyncFunction() &&
      !top_frame->function().IsAsyncGenerator()) {
    return false;
  }
  if (top_frame->IsInterpreted()) {
#if defined(DART_DYNAMIC_MODULES)
    const auto& bytecode = top_frame->bytecode();
    ASSERT(bytecode.HasSourcePositions());
    const uword pc_offset = top_frame->pc() - bytecode.PayloadStart();
    // pc_offset could equal to bytecode size if the last instruction is Throw.
    ASSERT(pc_offset <= static_cast<uword>(bytecode.Size()));
    bytecode::BytecodeSourcePositionsIterator iter(zone, bytecode);
    bool is_yield_point = false;
    while (iter.MoveNext()) {
      if (pc_offset <= iter.PcOffset()) {
        break;
      }
      is_yield_point = iter.IsYieldPoint();
    }
    return is_yield_point;
#else
    UNREACHABLE();
#endif
  } else {
    const TokenPosition looking_for = top_frame->TokenPos();
    const auto& pc_descriptors =
        PcDescriptors::Handle(zone, top_frame->code().pc_descriptors());
    if (!pc_descriptors.IsNull()) {
      PcDescriptors::Iterator it(pc_descriptors, UntaggedPcDescriptors::kOther);
      while (it.MoveNext()) {
        if (it.TokenPos() == looking_for &&
            it.YieldIndex() != UntaggedPcDescriptors::kInvalidYieldIndex) {
          return true;
        }
      }
    }
  }
  return false;
}

static bool IsAtBytecodeAsyncReturn(ActivationFrame* top_frame) {
#if defined(DART_DYNAMIC_MODULES)
  if (!top_frame->IsInterpreted()) return false;
  const auto& bytecode = top_frame->bytecode();
  const uword return_address = top_frame->pc();
  const uword prev = bytecode.GetInstructionBefore(return_address);
  if (prev == 0) {
    const uword start = bytecode.PayloadStart();
    // Async awaiter frames have an PC offset of 0 or 1.
    ASSERT(return_address == start ||
           return_address == start + StackTraceUtils::kFutureListenerPcOffset);
    return false;
  }
  auto* const instr = reinterpret_cast<const KBCInstr*>(prev);
  // Async returns in bytecode are implemented via direct calls to
  // the appropriate Dart async return method. Note that unlike other async
  // machinery, the direct call to _returnAsync is not marked synthetic.
  if (!KernelBytecode::IsDirectCallOpcode(instr)) {
    return false;
  }
  auto const index = KernelBytecode::DecodeD(instr);
  auto* const thread = Thread::Current();
  Zone* const zone = thread->zone();
  const auto& pool = ObjectPool::Handle(zone, bytecode.object_pool());
  const auto& obj = Object::Handle(zone, pool.ObjectAt(index));
  if (obj.IsNull() || !obj.IsFunction()) {
    return false;
  }
  const auto& target = Function::Cast(obj);
  auto* const object_store = thread->isolate_group()->object_store();
  return target.ptr() == object_store->suspend_state_return_async() ||
         target.ptr() ==
             object_store->suspend_state_return_async_not_future() ||
         target.ptr() == object_store->suspend_state_return_async_star();
#else
  return false;
#endif
}

#if defined(DART_DYNAMIC_MODULES)
static ActivationFrame::Relation CompareTopDartFrameTo(uword other_fp,
                                                       bool is_interpreted) {
  StackFrameIterator iterator(ValidationPolicy::kDontValidateFrames,
                              Thread::Current(),
                              StackFrameIterator::kNoCrossThreadIteration);
  for (auto* frame = iterator.NextFrame(); frame != nullptr;
       frame = iterator.NextFrame()) {
    if (frame->IsDartFrame() && (frame->is_interpreted() == is_interpreted)) {
      // The current frame's FP can be directly compared to the provided FP to
      // provide an answer, since they're using the same stack.
      //
      // Since this function is only called if the top Dart frame is interpreted
      // but the stepping frame is not or vice versa, the current frame is not
      // the top Dart frame, so a result of kSelf means the top Dart frame
      // is a callee.
      return ActivationFrame::CompareTo(is_interpreted, frame->fp(),
                                        other_fp) == ActivationFrame::kCaller
                 ? ActivationFrame::kCaller
                 : ActivationFrame::kCallee;
    }
  }
  // If there were no frames of the same type on the stack, this must have been
  // a caller of the original jump into the interpreter.
  ASSERT(is_interpreted);
  return ActivationFrame::kCaller;
}
#endif

ErrorPtr Debugger::PauseStepping() {
  ASSERT(Thread::Current()->single_step());
  // Don't pause recursively.
  if (IsPaused()) {
    return Error::null();
  }
  if (skip_next_step_) {
    skip_next_step_ = false;
    return Error::null();
  }

  ActivationFrame* frame = TopDartFrame();
  ASSERT(frame != nullptr);
  if (stepping_fp_ != 0) {
    // Check whether we are in a Dart function that the user is
    // interested in. If we saved the frame pointer of a stack frame
    // the user is interested in, we ignore the single step if we are
    // in a callee of that frame.
#if defined(DART_DYNAMIC_MODULES)
    auto const relation =
        stepping_fp_from_interpreted_frame_ == frame->IsInterpreted()
            ? frame->CompareTo(stepping_fp_)
            // Since interpreted code and compiled code use different stacks,
            // finding the relation to the top Dart frame requires some amount
            // of stack frame iteration.
            : CompareTopDartFrameTo(stepping_fp_,
                                    stepping_fp_from_interpreted_frame_);
#else
    // Note that we assume that the stack grows towards lower addresses.
    auto const relation = frame->CompareTo(stepping_fp_);
#endif
    // There is an "interesting frame" set. Only pause at appropriate
    // locations in this frame.
    if (relation == ActivationFrame::kCallee) {
      // We are in a callee of the frame we're interested in.
      // Ignore this stepping break.
      return Error::null();
    } else if (relation == ActivationFrame::kCaller) {
      // We returned from the "interesting frame", there can be no more
      // stepping breaks for it. Pause at the next appropriate location
      // and let the user set the "interesting" frame again.
      ResetSteppingFramePointer();
    }
  }

  if (!frame->IsDebuggable()) {
    return Error::null();
  }
  if (!frame->TokenPos().IsDebugPause()) {
    return Error::null();
  }

  if (frame->fp() == last_stepping_fp_ &&
      frame->TokenPos() == last_stepping_pos_) {
    // Do not stop multiple times for the same token position.
    // Several 'debug checked' opcodes may be issued in the same token range.
    return Error::null();
  }

  // TODO(dartbug.com/48378): Consider aligning async/async* functions
  // with regular function wrt the first stop in the function prologue.
  if ((frame->function().IsAsyncFunction() ||
       frame->function().IsAsyncGenerator()) &&
      frame->GetSuspendStateVar() == Object::null() &&
      // The bytecode generator sets the suspend state var to null prior
      // to returning.
      !IsAtBytecodeAsyncReturn(frame)) {
    return Error::null();
  }

  // We are stopping in this frame at the token pos.
  last_stepping_fp_ = frame->fp();
  last_stepping_pos_ = frame->TokenPos();

  // If there is an active breakpoint at this pc, then we should have
  // already bailed out of this function in the skip_next_step_ test
  // above.
  ASSERT(!group_debugger()->HasActiveBreakpoint(frame->pc()));

  if (FLAG_verbose_debug) {
    OS::PrintErr(">>> single step break at %s:%" Pd ":%" Pd
                 " (func %s token %s address %#" Px " offset %#" Px ")\n",
                 String::Handle(frame->SourceUrl()).ToCString(),
                 frame->LineNumber(), frame->ColumnNumber(),
                 String::Handle(frame->QualifiedFunctionName()).ToCString(),
                 frame->TokenPos().ToCString(), frame->pc(),
                 frame->pc() - frame->PayloadStart());
  }

  CacheStackTraces(DebuggerStackTrace::Collect(),
                   DebuggerStackTrace::CollectAsyncAwaiters());
  SignalPausedEvent(frame, nullptr);
  HandleSteppingRequest();
  ClearCachedStackTraces();

  // If any error occurred while in the debug message loop, return it here.
  return Thread::Current()->StealStickyError();
}

ErrorPtr Debugger::PauseBreakpoint() {
  // We ignore this breakpoint when the VM is executing code invoked
  // by the debugger to evaluate variables values, or when we see a nested
  // breakpoint or exception event.
  if (ignore_breakpoints_ || IsPaused()) {
    return Error::null();
  }
  DebuggerStackTrace* stack_trace = DebuggerStackTrace::Collect();
  ASSERT(stack_trace->Length() > 0);
  ActivationFrame* top_frame = stack_trace->FrameAt(0);
  ASSERT(top_frame != nullptr);
  if (!Library::Handle(top_frame->Library()).IsDebuggable()) {
    return Error::null();
  }

  BreakpointLocation* bpt_location = nullptr;
  const char* cbpt_tostring = nullptr;
  {
    SafepointReadRwLocker cbl(Thread::Current(),
                              group_debugger()->code_breakpoints_lock());
    CodeBreakpoint* cbpt = nullptr;
    bpt_location = group_debugger()->GetBreakpointLocationFor(
        this, top_frame->pc(), &cbpt);
    if (bpt_location == nullptr) {
      // There might be no breakpoint locations for this isolate/debugger.
      return Error::null();
    }
    ASSERT(cbpt != nullptr);
    if (FLAG_verbose_debug) {
      cbpt_tostring = cbpt->ToCString();
    }
  }

  Breakpoint* bpt_hit = bpt_location->FindHitBreakpoint(top_frame);
  if (bpt_hit == nullptr) {
    return Error::null();
  }

  if (FLAG_verbose_debug) {
    OS::PrintErr(">>> hit %" Pd
                 " %s"
                 " (func %s token %s address %#" Px " offset %#" Px ")\n",
                 bpt_hit->id(), cbpt_tostring,
                 String::Handle(top_frame->QualifiedFunctionName()).ToCString(),
                 bpt_location->token_pos().ToCString(), top_frame->pc(),
                 top_frame->pc() - top_frame->PayloadStart());
  }

  CacheStackTraces(stack_trace, DebuggerStackTrace::CollectAsyncAwaiters());
  SignalPausedEvent(top_frame, bpt_hit);
  // When we single step from a user breakpoint, our next stepping
  // point will be at the exact same pc.  Skip it.
  HandleSteppingRequest(/*skip_next_step=*/true);
  ClearCachedStackTraces();

  // If any error occurred while in the debug message loop, return it here.
  return Thread::Current()->StealStickyError();
}

Breakpoint* BreakpointLocation::FindHitBreakpoint(ActivationFrame* top_frame) {
  // There may be more than one applicable breakpoint at this location, but we
  // will report only one as reached. If there is a single-shot breakpoint, we
  // favor it; then a closure-specific breakpoint ; then an general breakpoint.

  // First check for a single-shot breakpoint.
  Breakpoint* bpt = breakpoints();
  while (bpt != nullptr) {
    if (bpt->is_single_shot() && bpt->closure() == Instance::null()) {
      return bpt;
    }
    bpt = bpt->next();
  }

  // Now check for a closure-specific breakpoint.
  bpt = breakpoints();
  while (bpt != nullptr) {
    if (bpt->closure() != Instance::null() &&
        bpt->closure() == top_frame->GetClosure()) {
      return bpt;
    }
    bpt = bpt->next();
  }

  // Finally, check for a general breakpoint.
  bpt = breakpoints();
  while (bpt != nullptr) {
    if (!bpt->is_single_shot() && bpt->closure() == Instance::null()) {
      return bpt;
    }
    bpt = bpt->next();
  }

  return nullptr;
}

void Debugger::PauseDeveloper(const String& msg) {
  // We ignore this breakpoint when the VM is executing code invoked
  // by the debugger to evaluate variables values, or when we see a nested
  // breakpoint or exception event.
  if (ignore_breakpoints_ || IsPaused()) {
    return;
  }

  DebuggerStackTrace* stack_trace = DebuggerStackTrace::Collect();
  ASSERT(stack_trace->Length() > 0);
  CacheStackTraces(stack_trace, DebuggerStackTrace::CollectAsyncAwaiters());

  // We are in the native call to Developer_debugger.  the developer
  // gets a better experience by not seeing this call. To accomplish
  // this, we continue execution until the call exits (step out).
  SetResumeAction(kStepOut);
  HandleSteppingRequest();
  ClearCachedStackTraces();
}

void Debugger::NotifyIsolateCreated() {
  if (NeedsIsolateEvents()) {
    ServiceEvent event(isolate_, ServiceEvent::kIsolateStart);
    InvokeEventHandler(&event);
  }
}

#if !defined(DART_PRECOMPILED_RUNTIME)
// |reference_pos_on_line| may specify the position of any column on a
// particular line. This function will return the position of column
// [column_number] on that same line.
static TokenPosition FindExactTokenPosition(const Script& script,
                                            TokenPosition reference_pos_on_line,
                                            intptr_t column_number) {
  intptr_t line;
  intptr_t col;
  if (script.GetTokenLocation(reference_pos_on_line, &line, &col)) {
    return TokenPosition::Deserialize(reference_pos_on_line.Pos() +
                                      (column_number - col));
  }
  return TokenPosition::kNoSource;
}
#endif  // !defined(DART_PRECOMPILED_RUNTIME)

void Debugger::NotifyDoneLoading() {
  if (latent_locations_ == nullptr) {
    // Common, fast path.
    return;
  }
  auto thread = Thread::Current();
  auto isolate_group = thread->isolate_group();
  auto zone = thread->zone();
  Library& lib = Library::Handle(zone);
  Script& script = Script::Handle(zone);
  String& url = String::Handle(zone);
  BreakpointLocation* loc = latent_locations_;
  BreakpointLocation* prev_loc = nullptr;
  const GrowableObjectArray& libs =
      GrowableObjectArray::Handle(isolate_group->object_store()->libraries());

  GrowableHandlePtrArray<const Script> scripts(zone, 1);
  while (loc != nullptr) {
    url = loc->url();
    bool found_match = false;
    bool is_package = url.StartsWith(Symbols::PackageScheme());
    for (intptr_t i = 0; i < libs.Length(); i++) {
      lib ^= libs.At(i);
      script = lib.LookupScript(url, !is_package);
      if (!script.IsNull()) {
        scripts.Add(script);
      }
    }
    if (scripts.length() > 0) {
      // Found a script with matching url for this latent breakpoint.
      // Unlink the latent breakpoint from the list.
      found_match = true;
      BreakpointLocation* matched_loc = loc;
      loc = loc->next();
      if (prev_loc == nullptr) {
        latent_locations_ = loc;
      } else {
        prev_loc->set_next(loc);
      }
      // Now find the token range at the requested line and make a
      // new unresolved source breakpoint.
      intptr_t line_number = matched_loc->requested_line_number();
      intptr_t column_number = matched_loc->requested_column_number();
      ASSERT(line_number >= 0);
      TokenPosition first_token_pos = TokenPosition::kNoSource;
      TokenPosition last_token_pos = TokenPosition::kNoSource;
      scripts.At(0).TokenRangeAtLine(line_number, &first_token_pos,
                                     &last_token_pos);
      if (!first_token_pos.IsDebugPause() || !last_token_pos.IsDebugPause()) {
        // Script does not contain the given line number or there are no
        // tokens on the line. Drop the breakpoint silently.
        Breakpoint* bpt = matched_loc->breakpoints();
        while (bpt != nullptr) {
          if (FLAG_verbose_debug) {
            OS::PrintErr("No code found at line %" Pd
                         ": "
                         "dropping latent breakpoint %" Pd " in '%s'\n",
                         line_number, bpt->id(), url.ToCString());
          }
          Breakpoint* prev = bpt;
          bpt = bpt->next();
          delete prev;
        }
        delete matched_loc;
      } else {
        // We don't expect to already have a breakpoint for this location.
        // If there is one, assert in debug build but silently drop
        // the latent breakpoint in release build.
        BreakpointLocation* existing_loc =
            GetBreakpointLocation(url, first_token_pos, -1, column_number);
        ASSERT(existing_loc == nullptr);
        if (existing_loc == nullptr) {
          // Create and register a new source breakpoint for the
          // latent breakpoint.
          BreakpointLocation* unresolved_loc = new BreakpointLocation(
              this, scripts, first_token_pos, last_token_pos, line_number,
              column_number);
          RegisterBreakpointLocation(unresolved_loc);

          // Move breakpoints over.
          Breakpoint* bpt = matched_loc->breakpoints();
          unresolved_loc->set_breakpoints(bpt);
          matched_loc->set_breakpoints(nullptr);
          while (bpt != nullptr) {
            bpt->set_bpt_location(unresolved_loc);
            if (FLAG_verbose_debug) {
              OS::PrintErr(
                  "Converted latent breakpoint "
                  "%" Pd " in '%s' at line %" Pd " col %" Pd "\n",
                  bpt->id(), url.ToCString(), line_number, column_number);
            }
            bpt = bpt->next();
          }
          group_debugger()->SyncBreakpointLocation(unresolved_loc);
        }
        delete matched_loc;
        // Break out of the iteration over loaded libraries. If the
        // same url has been loaded into more than one library, we
        // only set a breakpoint in the first one.
        // TODO(hausner): There is one possible pitfall here.
        // If the user sets a latent breakpoint using a partial url that
        // ends up matching more than one script, the breakpoint might
        // get set in the wrong script.
        // It would be better if we could warn the user if multiple
        // scripts are matching.
        break;
      }
    }
    if (!found_match) {
      // No matching url found in any of the libraries.
      if (FLAG_verbose_debug) {
        Breakpoint* bpt = loc->breakpoints();
        while (bpt != nullptr) {
          OS::PrintErr(
              "No match found for latent breakpoint id "
              "%" Pd " with url '%s'\n",
              bpt->id(), url.ToCString());
          bpt = bpt->next();
        }
      }
      loc = loc->next();
    }
  }
}

// TODO(hausner): Could potentially make this faster by checking
// whether the call target at pc is a debugger stub.
bool GroupDebugger::HasActiveBreakpoint(uword pc) {
  SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
  CodeBreakpoint* cbpt = GetCodeBreakpoint(pc);
  return (cbpt != nullptr) && (cbpt->IsEnabled());
}

CodeBreakpoint* GroupDebugger::GetCodeBreakpoint(uword breakpoint_address) {
  CodeBreakpoint* cbpt = code_breakpoints_;
  while (cbpt != nullptr) {
    if (cbpt->pc() == breakpoint_address) {
      return cbpt;
    }
    cbpt = cbpt->next();
  }
  return nullptr;
}

BreakpointLocation* GroupDebugger::GetBreakpointLocationFor(
    Debugger* debugger,
    uword breakpoint_address,
    CodeBreakpoint** pcbpt) {
  ASSERT(pcbpt != nullptr);
  SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
  *pcbpt = code_breakpoints_;
  while (*pcbpt != nullptr) {
    if ((*pcbpt)->pc() == breakpoint_address) {
      return (*pcbpt)->FindBreakpointForDebugger(debugger);
    }
    *pcbpt = (*pcbpt)->next();
  }
  return nullptr;
}

void GroupDebugger::RegisterCodeBreakpoint(CodeBreakpoint* cbpt) {
  ASSERT(cbpt->next() == nullptr);
  DEBUG_ASSERT(code_breakpoints_lock()->IsCurrentThreadWriter() ||
               Thread::Current()->IsInStoppedMutatorsScope());
  cbpt->set_next(code_breakpoints_);
  code_breakpoints_ = cbpt;
}

CodePtr GroupDebugger::GetPatchedStubAddress(uword breakpoint_address) {
  SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
  CodeBreakpoint* cbpt = GetCodeBreakpoint(breakpoint_address);
  if (cbpt != nullptr) {
    return cbpt->OrigStubAddress();
  }
  UNREACHABLE();
  return Code::null();
}

uint32_t GroupDebugger::GetPatchedOpcode(uword breakpoint_address) {
  SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
  CodeBreakpoint* cbpt = GetCodeBreakpoint(breakpoint_address);
  if (cbpt != nullptr) {
    return cbpt->OrigOpcode();
  }
  UNREACHABLE();
  return kMaxUint32;
}

bool Debugger::SetBreakpointState(Breakpoint* bpt, bool enable) {
  SafepointWriteRwLocker sl(Thread::Current(),
                            group_debugger()->breakpoint_locations_lock());
  if (bpt->is_enabled() != enable) {
    if (FLAG_verbose_debug) {
      OS::PrintErr("Setting breakpoint %" Pd " to state: %s\n", bpt->id(),
                   enable ? "enabled" : "disabled");
    }
    enable ? bpt->Enable() : bpt->Disable();
    group_debugger()->SyncBreakpointLocation(bpt->bpt_location());
    return true;
  }
  return false;
}

// Remove and delete the source breakpoint bpt and its associated
// code breakpoints.
void Debugger::RemoveBreakpoint(intptr_t bp_id) {
  SafepointWriteRwLocker sl(Thread::Current(),
                            group_debugger()->breakpoint_locations_lock());
  if (RemoveBreakpointFromTheList(bp_id, &breakpoint_locations_)) {
    return;
  }
  RemoveBreakpointFromTheList(bp_id, &latent_locations_);
}

// Remove and delete the source breakpoint bpt and its associated
// code breakpoints. Returns true, if breakpoint was found and removed,
// returns false, if breakpoint was not found.
bool Debugger::RemoveBreakpointFromTheList(intptr_t bp_id,
                                           BreakpointLocation** list) {
  BreakpointLocation* prev_loc = nullptr;
  BreakpointLocation* curr_loc = *list;
  while (curr_loc != nullptr) {
    Breakpoint* prev_bpt = nullptr;
    Breakpoint* curr_bpt = curr_loc->breakpoints();
    while (curr_bpt != nullptr) {
      if (curr_bpt->id() == bp_id) {
        if (prev_bpt == nullptr) {
          curr_loc->set_breakpoints(curr_bpt->next());
        } else {
          prev_bpt->set_next(curr_bpt->next());
        }

        // Send event to client before the breakpoint's fields are
        // poisoned and deleted.
        SendBreakpointEvent(ServiceEvent::kBreakpointRemoved, curr_bpt);

        curr_bpt->set_next(nullptr);
        curr_bpt->set_bpt_location(nullptr);
        // Remove possible references to the breakpoint.
        if (pause_event_ != nullptr && pause_event_->breakpoint() == curr_bpt) {
          pause_event_->set_breakpoint(nullptr);
        }
        delete curr_bpt;
        curr_bpt = nullptr;

        // Delete the breakpoint location object if there are no more
        // breakpoints at that location.
        if (curr_loc->breakpoints() == nullptr) {
          if (prev_loc == nullptr) {
            *list = curr_loc->next();
          } else {
            prev_loc->set_next(curr_loc->next());
          }

          if (!curr_loc->IsLatent()) {
            // Remove references from code breakpoints to this breakpoint
            // location and disable them.
            // Latent breakpoint locations won't have code breakpoints.
            group_debugger()->UnlinkCodeBreakpoints(curr_loc);
          }
          group_debugger()->UnregisterBreakpointLocation(curr_loc);
          BreakpointLocation* next_loc = curr_loc->next();
          delete curr_loc;
          curr_loc = next_loc;
        }

        // The code breakpoints will be deleted when the VM resumes
        // after the pause event.
        return true;
      }

      prev_bpt = curr_bpt;
      curr_bpt = curr_bpt->next();
    }
    prev_loc = curr_loc;
    curr_loc = curr_loc->next();
  }
  // breakpoint with bp_id does not exist, nothing to do.
  return false;
}

void GroupDebugger::RegisterBreakpointLocation(BreakpointLocation* location) {
  DEBUG_ASSERT(breakpoint_locations_lock()->IsCurrentThreadWriter() ||
               Thread::Current()->IsInStoppedMutatorsScope());
  breakpoint_locations_.Add(location);
}

void GroupDebugger::UnregisterBreakpointLocation(BreakpointLocation* location) {
  ASSERT(breakpoint_locations_lock()->IsCurrentThreadWriter());
  for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
    if (breakpoint_locations_.At(i) == location) {
      breakpoint_locations_.EraseAt(i);
      return;
    }
  }
}

// Unlink code breakpoints from the given breakpoint location.
// They will later be deleted when control returns from the pause event
// callback. Also, disable the breakpoint so it no longer fires if it
// should be hit before it gets deleted.
void GroupDebugger::UnlinkCodeBreakpoints(BreakpointLocation* bpt_location) {
  ASSERT(bpt_location != nullptr);
  SafepointWriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
  CodeBreakpoint* curr_bpt = code_breakpoints_;
  while (curr_bpt != nullptr) {
    if (curr_bpt->FindAndDeleteBreakpointLocation(bpt_location)) {
      curr_bpt->Disable();
      needs_breakpoint_cleanup_ = true;
    }
    curr_bpt = curr_bpt->next();
  }
}

// Remove and delete unlinked code breakpoints, i.e. breakpoints that
// are not associated with a breakpoint location.
void GroupDebugger::RemoveUnlinkedCodeBreakpoints() {
  DEBUG_ASSERT(code_breakpoints_lock()->IsCurrentThreadWriter() ||
               Thread::Current()->IsInStoppedMutatorsScope());
  CodeBreakpoint* prev_bpt = nullptr;
  CodeBreakpoint* curr_bpt = code_breakpoints_;
  while (curr_bpt != nullptr) {
    if (curr_bpt->HasNoBreakpointLocations()) {
      if (prev_bpt == nullptr) {
        code_breakpoints_ = code_breakpoints_->next();
      } else {
        prev_bpt->set_next(curr_bpt->next());
      }
      CodeBreakpoint* temp_bpt = curr_bpt;
      curr_bpt = curr_bpt->next();
      delete temp_bpt;
    } else {
      prev_bpt = curr_bpt;
      curr_bpt = curr_bpt->next();
    }
  }
  needs_breakpoint_cleanup_ = false;
}

BreakpointLocation* Debugger::GetResolvedBreakpointLocation(
    const String& script_url,
    TokenPosition code_token_pos) {
  BreakpointLocation* loc = breakpoint_locations_;
  String& loc_url = String::Handle();
  while (loc != nullptr) {
    loc_url = loc->url();
    if (script_url.Equals(loc_url) && loc->code_token_pos_ == code_token_pos) {
      return loc;
    }
    loc = loc->next();
  }
  return nullptr;
}

BreakpointLocation* Debugger::GetBreakpointLocation(
    const String& script_url,
    TokenPosition token_pos,
    intptr_t requested_line,
    intptr_t requested_column,
    TokenPosition code_token_pos) {
  BreakpointLocation* loc = breakpoint_locations_;
  String& loc_url = String::Handle();
  while (loc != nullptr) {
    loc_url = loc->url();
    if (script_url.Equals(loc_url) &&
        (!token_pos.IsReal() || (loc->token_pos() == token_pos)) &&
        ((requested_line == -1) ||
         (loc->requested_line_number_ == requested_line)) &&
        ((requested_column == -1) ||
         (loc->requested_column_number_ == requested_column)) &&
        (!code_token_pos.IsReal() ||
         (loc->code_token_pos_ == code_token_pos))) {
      return loc;
    }
    loc = loc->next();
  }
  return nullptr;
}

Breakpoint* Debugger::GetBreakpointById(intptr_t id) {
  Breakpoint* bpt = GetBreakpointByIdInTheList(id, breakpoint_locations_);
  if (bpt != nullptr) {
    return bpt;
  }
  return GetBreakpointByIdInTheList(id, latent_locations_);
}

Breakpoint* Debugger::GetBreakpointByIdInTheList(intptr_t id,
                                                 BreakpointLocation* list) {
  BreakpointLocation* loc = list;
  while (loc != nullptr) {
    Breakpoint* bpt = loc->breakpoints();
    while (bpt != nullptr) {
      if (bpt->id() == id) {
        return bpt;
      }
      bpt = bpt->next();
    }
    loc = loc->next();
  }
  return nullptr;
}

void Debugger::AsyncStepInto(const Closure& awaiter) {
  Zone* zone = Thread::Current()->zone();

  auto& suspend_state = SuspendState::Handle(zone);
  if (StackTraceUtils::GetSuspendState(awaiter, &suspend_state)) {
    const auto& function_data =
        Object::Handle(zone, suspend_state.function_data());
    SetBreakpointAtResumption(function_data);
  } else {
    // We intentionally discard the value stored into the following variable.
    Breakpoint* _ = nullptr;
    const Error& error = Error::Handle(
        SetBreakpointAtActivation(awaiter, /*single_shot=*/true, &_));
    // This method cannot be called while responding to a Service RPC, so we
    // know that the top error handler is an exit frame.
    DEBUG_ASSERT(Thread::Current()->TopErrorHandlerIsExitFrame());
    if (!error.IsNull()) {
      if (error.ptr() == Object::out_of_memory_error().ptr()) {
        Exceptions::ThrowOOM();
        UNREACHABLE();
      }
      if (error.IsLanguageError()) {
        Exceptions::ThrowCompileTimeError(LanguageError::Cast(error));
        UNREACHABLE();
      }
      Exceptions::PropagateError(Error::Cast(error));
      UNREACHABLE();
    }
  }
  Continue();
}

void Debugger::Continue() {
  SetResumeAction(kContinue);
  ResetSteppingFramePointer();
  NotifySingleStepping(false);
}

BreakpointLocation* Debugger::GetLatentBreakpoint(const String& url,
                                                  intptr_t line,
                                                  intptr_t column) {
  BreakpointLocation* loc = latent_locations_;
  String& bpt_url = String::Handle();
  while (loc != nullptr) {
    bpt_url = loc->url();
    if (bpt_url.Equals(url) && (loc->requested_line_number() == line) &&
        (loc->requested_column_number() == column)) {
      return loc;
    }
    loc = loc->next();
  }
  // No breakpoint for this location requested. Allocate new one.
  loc = new BreakpointLocation(this, url, line, column);
  loc->set_next(latent_locations_);
  latent_locations_ = loc;
  return loc;
}

void Debugger::RegisterBreakpointLocationUnsafe(BreakpointLocation* loc) {
  DEBUG_ASSERT(
      group_debugger()->breakpoint_locations_lock()->IsCurrentThreadWriter() ||
      Thread::Current()->IsInStoppedMutatorsScope());
  ASSERT(loc->next() == nullptr);
  loc->set_next(breakpoint_locations_);
  breakpoint_locations_ = loc;
  group_debugger()->RegisterBreakpointLocation(loc);
}

void Debugger::RegisterBreakpointLocation(BreakpointLocation* loc) {
  auto thread = Thread::Current();
  if (thread->IsInStoppedMutatorsScope()) {
    RegisterBreakpointLocationUnsafe(loc);
  } else {
    SafepointWriteRwLocker sl(thread,
                              group_debugger()->breakpoint_locations_lock());
    RegisterBreakpointLocationUnsafe(loc);
  }
}

#endif  // !PRODUCT

}  // namespace dart
